Skip to content

Commit 964dea4

Browse files
committed
build: more and better unit tests
1 parent d66ff86 commit 964dea4

12 files changed

Lines changed: 2478 additions & 401 deletions

jooby/src/main/java/io/jooby/DefaultContext.java

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,9 @@ default Session session() {
177177

178178
@Override
179179
default Object forward(String path) {
180-
try {
181-
setRequestPath(path);
182-
Router.Match match = getRouter().match(this);
183-
return match.execute(this, match.route().getHandler());
184-
} catch (Throwable cause) {
185-
throw SneakyThrows.propagate(cause);
186-
}
180+
setRequestPath(path);
181+
Router.Match match = getRouter().match(this);
182+
return match.execute(this, match.route().getHandler());
187183
}
188184

189185
@Override
@@ -421,24 +417,18 @@ default int getServerPort() {
421417
@Override
422418
default int getPort() {
423419
var hostAndPort = getHostAndPort();
424-
if (hostAndPort != null) {
425-
int index = hostAndPort.indexOf(':');
426-
if (index > 0) {
427-
return Integer.parseInt(hostAndPort.substring(index + 1));
428-
}
429-
return isSecure() ? SECURE_PORT : PORT;
420+
int index = hostAndPort.indexOf(':');
421+
if (index > 0) {
422+
return Integer.parseInt(hostAndPort.substring(index + 1));
430423
}
431-
return getServerPort();
424+
return isSecure() ? SECURE_PORT : PORT;
432425
}
433426

434427
@Override
435428
default String getHost() {
436429
String hostAndPort = getHostAndPort();
437-
if (hostAndPort != null) {
438-
int index = hostAndPort.indexOf(':');
439-
return index > 0 ? hostAndPort.substring(0, index).trim() : hostAndPort;
440-
}
441-
return getServerHost();
430+
int index = hostAndPort.indexOf(':');
431+
return index > 0 ? hostAndPort.substring(0, index).trim() : hostAndPort;
442432
}
443433

444434
@Override

jooby/src/main/java/io/jooby/Environment.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@
88
import java.nio.file.Files;
99
import java.nio.file.Path;
1010
import java.nio.file.Paths;
11-
import java.util.Collections;
12-
import java.util.HashMap;
13-
import java.util.List;
14-
import java.util.Map;
15-
import java.util.Optional;
11+
import java.util.*;
1612
import java.util.stream.Collectors;
1713
import java.util.stream.Stream;
1814

@@ -21,7 +17,6 @@
2117
import com.typesafe.config.Config;
2218
import com.typesafe.config.ConfigException;
2319
import com.typesafe.config.ConfigFactory;
24-
import com.typesafe.config.ConfigParseOptions;
2520

2621
/**
2722
* Application environment contains configuration object and active environment names.
@@ -244,9 +239,7 @@ private static boolean hasPath(Config config, String key) {
244239
* @return Configuration object.
245240
*/
246241
public static Config systemProperties() {
247-
return ConfigFactory.parseProperties(
248-
System.getProperties(),
249-
ConfigParseOptions.defaults().setOriginDescription("system properties"));
242+
return ConfigFactory.systemProperties();
250243
}
251244

252245
/**

jooby/src/test/java/io/jooby/CookieTest.java

Lines changed: 223 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,38 @@
77

88
import static org.junit.jupiter.api.Assertions.assertEquals;
99
import static org.junit.jupiter.api.Assertions.assertFalse;
10+
import static org.junit.jupiter.api.Assertions.assertNull;
1011
import static org.junit.jupiter.api.Assertions.assertThrows;
12+
import static org.junit.jupiter.api.Assertions.assertTrue;
13+
import static org.mockito.ArgumentMatchers.anyString;
14+
import static org.mockito.Mockito.mockStatic;
15+
16+
import java.io.UnsupportedEncodingException;
17+
import java.net.URLDecoder;
18+
import java.net.URLEncoder;
19+
import java.time.Duration;
20+
import java.util.Optional;
1121

1222
import org.junit.jupiter.api.Test;
23+
import org.mockito.MockedStatic;
1324

1425
import com.google.common.collect.ImmutableMap;
26+
import com.typesafe.config.Config;
1527
import com.typesafe.config.ConfigFactory;
1628

1729
public class CookieTest {
1830

1931
@Test
2032
public void encodeDecode() {
2133
assertEquals("", Cookie.encode(ImmutableMap.of()));
34+
assertEquals("", Cookie.encode(null));
2235
assertEquals(ImmutableMap.of(), Cookie.decode(""));
36+
assertEquals(ImmutableMap.of(), Cookie.decode(null));
2337

2438
assertEquals("foo=bar", Cookie.encode(ImmutableMap.of("foo", "bar")));
2539
assertEquals(ImmutableMap.of(), Cookie.decode("foo"));
2640
assertEquals(ImmutableMap.of(), Cookie.decode("foo="));
41+
assertEquals(ImmutableMap.of(), Cookie.decode("=foo")); // eq == 0
2742
assertEquals(ImmutableMap.of("foo", "b"), Cookie.decode("foo=b"));
2843
assertEquals(ImmutableMap.of("foo", "bar"), Cookie.decode("foo=bar"));
2944
assertEquals(ImmutableMap.of("foo", "bar"), Cookie.decode("foo=bar&"));
@@ -35,6 +50,27 @@ public void encodeDecode() {
3550
assertEquals("foo=bar&x=y", Cookie.encode(ImmutableMap.of("foo", "bar", "x", "y")));
3651
}
3752

53+
@Test
54+
public void encodeDecodeExceptions() {
55+
// Tests the unreachable UnsupportedEncodingException catch blocks using Mockito
56+
try (MockedStatic<URLEncoder> encoder = mockStatic(URLEncoder.class);
57+
MockedStatic<URLDecoder> decoder = mockStatic(URLDecoder.class)) {
58+
59+
encoder
60+
.when(() -> URLEncoder.encode(anyString(), anyString()))
61+
.thenThrow(new UnsupportedEncodingException("mock error"));
62+
63+
assertThrows(
64+
UnsupportedEncodingException.class, () -> Cookie.encode(ImmutableMap.of("a", "b")));
65+
66+
decoder
67+
.when(() -> URLDecoder.decode(anyString(), anyString()))
68+
.thenThrow(new UnsupportedEncodingException("mock error"));
69+
70+
assertThrows(UnsupportedEncodingException.class, () -> Cookie.decode("a=b"));
71+
}
72+
}
73+
3874
@Test
3975
public void signUnsign() {
4076
assertEquals(
@@ -53,6 +89,188 @@ public void signUnsign() {
5389
"RcFzlzECN2Lv32Ie9jfSWVr13j6OjllJwDDZe4mVS4c|foo=bar&x=u iq", "987654345!$009P"));
5490
}
5591

92+
@Test
93+
public void signUnsignEdgeCasesAndExceptions() {
94+
String secret = "987654345!$009P";
95+
assertNull(Cookie.unsign("noseparator", secret));
96+
assertNull(Cookie.unsign("|emptyvalue", secret));
97+
assertNull(Cookie.unsign("invalidHash|foo=bar", secret));
98+
99+
// Force exception in sign block (NullPointerException wrapped in SneakyThrows)
100+
assertThrows(RuntimeException.class, () -> Cookie.sign(null, secret));
101+
}
102+
103+
@Test
104+
public void testConstructorsGettersSettersAndClone() {
105+
Cookie c = new Cookie("sid");
106+
assertEquals("sid", c.getName());
107+
assertNull(c.getValue());
108+
109+
c.setValue("123");
110+
assertEquals("123", c.getValue());
111+
112+
assertNull(c.getDomain());
113+
assertEquals("def.com", c.getDomain("def.com"));
114+
c.setDomain("foo.com");
115+
assertEquals("foo.com", c.getDomain());
116+
assertEquals("foo.com", c.getDomain("def.com"));
117+
118+
assertNull(c.getPath());
119+
assertEquals("/def", c.getPath("/def"));
120+
c.setPath("/api");
121+
assertEquals("/api", c.getPath());
122+
assertEquals("/api", c.getPath("/def"));
123+
124+
assertFalse(c.isHttpOnly());
125+
c.setHttpOnly(true);
126+
assertTrue(c.isHttpOnly());
127+
128+
assertFalse(c.isSecure());
129+
c.setSecure(true);
130+
assertTrue(c.isSecure());
131+
132+
assertEquals(-1, c.getMaxAge());
133+
c.setMaxAge(Duration.ofSeconds(60));
134+
assertEquals(60, c.getMaxAge());
135+
136+
c.setMaxAge(-5);
137+
assertEquals(-1, c.getMaxAge());
138+
139+
c.setSameSite(SameSite.STRICT);
140+
assertEquals(SameSite.STRICT, c.getSameSite());
141+
142+
c.setName("new-name");
143+
assertEquals("new-name", c.getName());
144+
145+
Cookie cloned = c.clone();
146+
assertEquals(c.getName(), cloned.getName());
147+
assertEquals(c.getValue(), cloned.getValue());
148+
assertEquals(c.getDomain(), cloned.getDomain());
149+
assertEquals(c.getPath(), cloned.getPath());
150+
assertEquals(c.isHttpOnly(), cloned.isHttpOnly());
151+
assertEquals(c.isSecure(), cloned.isSecure());
152+
assertEquals(c.getMaxAge(), cloned.getMaxAge());
153+
assertEquals(c.getSameSite(), cloned.getSameSite());
154+
}
155+
156+
@Test
157+
public void testToStringAndToCookieString() {
158+
Cookie c = new Cookie("foo");
159+
assertEquals("foo=", c.toString());
160+
assertEquals("foo=", c.toCookieString());
161+
162+
c.setValue("bar");
163+
assertEquals("foo=bar", c.toString());
164+
165+
c.setPath("/");
166+
c.setDomain("example.com");
167+
c.setSameSite(SameSite.LAX);
168+
c.setSecure(true);
169+
c.setHttpOnly(true);
170+
c.setMaxAge(0);
171+
172+
String cs = c.toCookieString();
173+
assertTrue(cs.contains("foo=bar"));
174+
assertTrue(cs.contains("Path=/"));
175+
assertTrue(cs.contains("Domain=example.com"));
176+
assertTrue(cs.contains("SameSite=Lax"));
177+
assertTrue(cs.contains("Secure"));
178+
assertTrue(cs.contains("HttpOnly"));
179+
assertTrue(cs.contains("Max-Age=0"));
180+
assertTrue(cs.contains("Expires=Thu, 01-Jan-1970 00:00:00 GMT")); // Check exact 0 expiration
181+
182+
c.setMaxAge(3600); // maxAge > 0 branch
183+
cs = c.toCookieString();
184+
assertTrue(cs.contains("Max-Age=3600"));
185+
assertTrue(cs.contains("Expires="));
186+
187+
c.setMaxAge(-1); // maxAge < 0 branch
188+
cs = c.toCookieString();
189+
assertFalse(cs.contains("Max-Age"));
190+
assertFalse(cs.contains("Expires"));
191+
}
192+
193+
@Test
194+
public void testQuotesAndEscaping() {
195+
Cookie c = new Cookie("foo", "v a l u e"); // Space needs quotes
196+
assertEquals("foo=\"v a l u e\"", c.toCookieString());
197+
198+
c = new Cookie("foo", "val,ue"); // Comma
199+
assertEquals("foo=\"val,ue\"", c.toCookieString());
200+
201+
c = new Cookie("foo", "val;ue"); // Semicolon
202+
assertEquals("foo=\"val;ue\"", c.toCookieString());
203+
204+
c = new Cookie("foo", "val\\ue"); // Backslash -> gets escaped
205+
assertEquals("foo=\"val\\\\ue\"", c.toCookieString());
206+
207+
c = new Cookie("foo", "val\"ue"); // Double quote -> gets escaped
208+
assertEquals("foo=\"val\\\"ue\"", c.toCookieString());
209+
210+
c = new Cookie("foo", "val\tue"); // Tab
211+
assertEquals("foo=\"val\tue\"", c.toCookieString());
212+
213+
// Already quoted -> doesn't need quotes if properly enclosed
214+
c = new Cookie("foo", "\"already quoted\"");
215+
assertEquals("foo=\"already quoted\"", c.toCookieString());
216+
217+
// Already quoted but effectively empty body -> length condition edge case
218+
c = new Cookie("foo", "\"\"");
219+
assertEquals("foo=\"\"", c.toCookieString());
220+
221+
// Single quote char requires escaping and surrounding quotes
222+
c = new Cookie("foo", "\"");
223+
assertEquals("foo=\"\\\"\"", c.toCookieString());
224+
}
225+
226+
@Test
227+
public void testSessionCookieFactory() {
228+
Cookie session = Cookie.session("my-sid");
229+
assertEquals("my-sid", session.getName());
230+
assertNull(session.getValue());
231+
assertEquals(-1, session.getMaxAge());
232+
assertTrue(session.isHttpOnly());
233+
assertEquals("/", session.getPath());
234+
}
235+
236+
@Test
237+
public void testCreateFromConfig() {
238+
Config config =
239+
ConfigFactory.parseMap(
240+
ImmutableMap.<String, Object>builder()
241+
.put("session.name", "sid")
242+
.put("session.value", "123")
243+
.put("session.path", "/app")
244+
.put("session.domain", "localhost")
245+
.put("session.secure", false)
246+
.put("session.httpOnly", true)
247+
.put("session.maxAge", "1h")
248+
.put("session.sameSite", "Strict")
249+
.build());
250+
251+
Optional<Cookie> cOpt = Cookie.create("session", config);
252+
assertTrue(cOpt.isPresent());
253+
254+
Cookie c = cOpt.get();
255+
assertEquals("sid", c.getName());
256+
assertEquals("123", c.getValue());
257+
assertEquals("/app", c.getPath());
258+
assertEquals("localhost", c.getDomain());
259+
assertFalse(c.isSecure());
260+
assertTrue(c.isHttpOnly());
261+
assertEquals(3600, c.getMaxAge());
262+
assertEquals(SameSite.STRICT, c.getSameSite());
263+
264+
// Empty config namespace branch
265+
assertFalse(Cookie.create("missing", config).isPresent());
266+
267+
// Partial config namespace branch (missing optional values)
268+
Config partialConfig = ConfigFactory.parseMap(ImmutableMap.of("session.name", "partial"));
269+
Cookie partialCookie = Cookie.create("session", partialConfig).get();
270+
assertEquals("partial", partialCookie.getName());
271+
assertNull(partialCookie.getValue());
272+
}
273+
56274
@Test
57275
public void testCreateSameSite() {
58276
assertEquals(
@@ -140,9 +358,13 @@ public void testSameSiteVsSecure() {
140358
+ " allowing non-secure cookies before calling Cookie.setSecure(false).",
141359
t2.getMessage());
142360

143-
cookie.setSameSite(null);
361+
// Allows secure = false if SameSite does not mandate it
362+
cookie.setSameSite(SameSite.LAX);
144363
cookie.setSecure(false);
364+
assertFalse(cookie.isSecure());
145365

366+
cookie.setSameSite(null);
367+
cookie.setSecure(false);
146368
assertFalse(cookie.isSecure());
147369
}
148370
}

0 commit comments

Comments
 (0)