Skip to content

Commit e4bae2e

Browse files
committed
build: more unit tests
1 parent 1856b00 commit e4bae2e

14 files changed

Lines changed: 1180 additions & 88 deletions

jooby/src/main/java/io/jooby/OpenAPIModule.java

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
*/
66
package io.jooby;
77

8-
import java.io.ByteArrayInputStream;
9-
import java.io.InputStream;
108
import java.nio.charset.StandardCharsets;
119
import java.util.*;
1210

@@ -16,6 +14,7 @@
1614
import io.jooby.handler.Asset;
1715
import io.jooby.handler.AssetSource;
1816
import io.jooby.internal.IOUtils;
17+
import io.jooby.internal.OpenAPIAsset;
1918

2019
/**
2120
* OpenAPI supports for Jooby. Basic Usage:
@@ -39,54 +38,9 @@
3938
*/
4039
public class OpenAPIModule implements Extension {
4140

42-
private static class OpenAPIAsset implements Asset {
43-
44-
private final long lastModified;
45-
46-
private final byte[] content;
47-
48-
private final MediaType type;
49-
50-
OpenAPIAsset(MediaType type, byte[] content, long lastModified) {
51-
this.content = content;
52-
this.type = type;
53-
this.lastModified = lastModified;
54-
}
55-
56-
@Override
57-
public long getSize() {
58-
return content.length;
59-
}
60-
61-
@Override
62-
public long getLastModified() {
63-
return lastModified;
64-
}
65-
66-
@Override
67-
public boolean isDirectory() {
68-
return false;
69-
}
70-
71-
@Override
72-
public MediaType getContentType() {
73-
return type;
74-
}
75-
76-
@Override
77-
public InputStream stream() {
78-
return new ByteArrayInputStream(content);
79-
}
80-
81-
@Override
82-
public void close() throws Exception {
83-
// NOOP
84-
}
85-
}
86-
8741
private static class OpenAPISource implements AssetSource {
8842

89-
private Map<String, Asset> assets = new HashMap<>();
43+
private final Map<String, Asset> assets = new HashMap<>();
9044

9145
public OpenAPISource put(String key, Asset asset) {
9246
assets.put(key, asset);

jooby/src/main/java/io/jooby/Route.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,37 @@ public MethodHandle toMethodHandle(MethodHandles.Lookup lookup) {
447447
public MethodHandle toMethodHandle() {
448448
return toMethodHandle(MethodHandles.publicLookup());
449449
}
450+
451+
@Override
452+
public boolean equals(Object o) {
453+
if (this == o) return true;
454+
if (!(o instanceof MvcMethod that)) return false;
455+
return declaringClass.equals(that.declaringClass)
456+
&& name.equals(that.name)
457+
&& returnType.equals(that.returnType)
458+
&& Arrays.equals(parameterTypes, that.parameterTypes);
459+
}
460+
461+
@Override
462+
public int hashCode() {
463+
int result = Objects.hash(declaringClass, name, returnType);
464+
result = 31 * result + Arrays.hashCode(parameterTypes);
465+
return result;
466+
}
467+
468+
@Override
469+
public String toString() {
470+
return "MvcMethod["
471+
+ "declaringClass="
472+
+ declaringClass
473+
+ ", name="
474+
+ name
475+
+ ", returnType="
476+
+ returnType
477+
+ ", parameterTypes="
478+
+ Arrays.toString(parameterTypes)
479+
+ ']';
480+
}
450481
}
451482

452483
/** Favicon handler as a silent 404 error. */
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal;
7+
8+
import java.io.ByteArrayInputStream;
9+
import java.io.InputStream;
10+
11+
import io.jooby.MediaType;
12+
import io.jooby.handler.Asset;
13+
14+
public class OpenAPIAsset implements Asset {
15+
16+
private final long lastModified;
17+
18+
private final byte[] content;
19+
20+
private final MediaType type;
21+
22+
public OpenAPIAsset(MediaType type, byte[] content, long lastModified) {
23+
this.content = content;
24+
this.type = type;
25+
this.lastModified = lastModified;
26+
}
27+
28+
@Override
29+
public long getSize() {
30+
return content.length;
31+
}
32+
33+
@Override
34+
public long getLastModified() {
35+
return lastModified;
36+
}
37+
38+
@Override
39+
public boolean isDirectory() {
40+
return false;
41+
}
42+
43+
@Override
44+
public MediaType getContentType() {
45+
return type;
46+
}
47+
48+
@Override
49+
public InputStream stream() {
50+
return new ByteArrayInputStream(content);
51+
}
52+
53+
@Override
54+
public void close() throws Exception {
55+
// NOOP
56+
}
57+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertFalse;
10+
import static org.junit.jupiter.api.Assertions.assertThrows;
11+
import static org.junit.jupiter.api.Assertions.assertTrue;
12+
import static org.mockito.ArgumentMatchers.any;
13+
import static org.mockito.Mockito.mock;
14+
import static org.mockito.Mockito.verify;
15+
import static org.mockito.Mockito.when;
16+
17+
import java.io.InputStream;
18+
import java.lang.reflect.Type;
19+
import java.nio.charset.StandardCharsets;
20+
import java.nio.file.Path;
21+
import java.nio.file.Paths;
22+
import java.util.Iterator;
23+
import java.util.List;
24+
import java.util.Set;
25+
26+
import org.junit.jupiter.api.DisplayName;
27+
import org.junit.jupiter.api.Test;
28+
29+
import io.jooby.exception.MissingValueException;
30+
import io.jooby.value.Value;
31+
32+
public class BodyTest {
33+
34+
@Test
35+
@DisplayName("Verify value(Charset) decoding branches")
36+
void testValueCharset() {
37+
Body body = mock(Body.class);
38+
when(body.value(any(java.nio.charset.Charset.class))).thenCallRealMethod();
39+
40+
// Branch 1: Missing value (empty byte array)
41+
when(body.bytes()).thenReturn(new byte[0]);
42+
assertThrows(MissingValueException.class, () -> body.value(StandardCharsets.UTF_8));
43+
44+
// Branch 2: Successful decode
45+
String testContent = "jooby-body";
46+
when(body.bytes()).thenReturn(testContent.getBytes(StandardCharsets.UTF_8));
47+
assertEquals(testContent, body.value(StandardCharsets.UTF_8));
48+
}
49+
50+
@Test
51+
@DisplayName("Verify size() returns 1 as defined by default method")
52+
void testSize() {
53+
Body body = mock(Body.class);
54+
when(body.size()).thenCallRealMethod();
55+
assertEquals(1, body.size());
56+
}
57+
58+
@Test
59+
@DisplayName("Verify get(int) delegates to get(String)")
60+
void testGetInt() {
61+
Body body = mock(Body.class);
62+
Value expectedValue = mock(Value.class);
63+
64+
when(body.get("5")).thenReturn(expectedValue);
65+
when(body.get(5)).thenCallRealMethod();
66+
67+
assertEquals(expectedValue, body.get(5));
68+
verify(body).get("5");
69+
}
70+
71+
@Test
72+
@DisplayName("Verify iterator wraps the body instance")
73+
void testIterator() {
74+
Body body = mock(Body.class);
75+
when(body.iterator()).thenCallRealMethod();
76+
77+
Iterator<Value> iterator = body.iterator();
78+
79+
assertTrue(iterator.hasNext());
80+
assertEquals(body, iterator.next());
81+
assertFalse(iterator.hasNext());
82+
}
83+
84+
@Test
85+
@DisplayName("Verify toList(Class) delegates to to(Type) via Reified")
86+
void testToListClass() {
87+
Body body = mock(Body.class);
88+
List<String> expectedList = List.of("a", "b");
89+
90+
// We capture the Reified Type delegation
91+
when(body.to(any(Type.class))).thenReturn(expectedList);
92+
when(body.toList(String.class)).thenCallRealMethod();
93+
94+
assertEquals(expectedList, body.toList(String.class));
95+
}
96+
97+
@Test
98+
@DisplayName("Verify string-based toList() and toSet() collections")
99+
void testStringCollections() {
100+
Body body = mock(Body.class);
101+
when(body.value()).thenReturn("test-value");
102+
when(body.toList()).thenCallRealMethod();
103+
when(body.toSet()).thenCallRealMethod();
104+
105+
assertEquals(List.of("test-value"), body.toList());
106+
assertEquals(Set.of("test-value"), body.toSet());
107+
}
108+
109+
@Test
110+
@DisplayName("Verify class-based conversions delegate to type-based conversions")
111+
void testClassConversions() {
112+
Body body = mock(Body.class);
113+
114+
when(body.to((Type) Integer.class)).thenReturn(100);
115+
when(body.to(Integer.class)).thenCallRealMethod();
116+
assertEquals(100, body.to(Integer.class));
117+
118+
when(body.toNullable((Type) Long.class)).thenReturn(200L);
119+
when(body.toNullable(Long.class)).thenCallRealMethod();
120+
assertEquals(200L, body.toNullable(Long.class));
121+
}
122+
123+
@Test
124+
@DisplayName("Verify static factory methods route to correct internal implementations")
125+
void testStaticFactories() {
126+
Context ctx = mock(Context.class);
127+
128+
// Using getClass().getSimpleName() avoids restricted visibility issues
129+
// if internal classes are package-private while ensuring the correct factory was invoked.
130+
131+
Body emptyBody = Body.empty(ctx);
132+
assertEquals("ByteArrayBody", emptyBody.getClass().getSimpleName());
133+
134+
Body bytesBody = Body.of(ctx, new byte[] {1, 2, 3});
135+
assertEquals("ByteArrayBody", bytesBody.getClass().getSimpleName());
136+
137+
InputStream stream = mock(InputStream.class);
138+
Body streamBody = Body.of(ctx, stream, 1024L);
139+
assertEquals("InputStreamBody", streamBody.getClass().getSimpleName());
140+
141+
Path file = Paths.get("dummy.txt");
142+
Body fileBody = Body.of(ctx, file);
143+
assertEquals("FileBody", fileBody.getClass().getSimpleName());
144+
}
145+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertSame;
10+
import static org.junit.jupiter.api.Assertions.assertTrue;
11+
12+
import java.util.HashMap;
13+
import java.util.Locale;
14+
import java.util.Map;
15+
16+
import org.junit.jupiter.api.DisplayName;
17+
import org.junit.jupiter.api.Test;
18+
19+
public class MapModelAndViewTest {
20+
21+
@Test
22+
@DisplayName("Verify constructor with view and provided model")
23+
void testConstructorWithViewAndModel() {
24+
Map<String, Object> initialModel = new HashMap<>();
25+
initialModel.put("key1", "value1");
26+
27+
MapModelAndView mav = new MapModelAndView("index.html", initialModel);
28+
29+
assertEquals("index.html", mav.getView());
30+
assertSame(initialModel, mav.getModel());
31+
}
32+
33+
@Test
34+
@DisplayName("Verify constructor with view only initializes an empty LinkedHashMap")
35+
void testConstructorWithViewOnly() {
36+
MapModelAndView mav = new MapModelAndView("index.html");
37+
38+
assertEquals("index.html", mav.getView());
39+
assertTrue(mav.getModel().isEmpty());
40+
assertEquals("java.util.LinkedHashMap", mav.getModel().getClass().getName());
41+
}
42+
43+
@Test
44+
@DisplayName("Verify put(String, Object) adds to model and returns this")
45+
void testPutSingleAttribute() {
46+
MapModelAndView mav = new MapModelAndView("index.html");
47+
48+
MapModelAndView result = mav.put("foo", "bar");
49+
50+
assertSame(mav, result, "put should return the current instance for fluent chaining");
51+
assertEquals(1, mav.getModel().size());
52+
assertEquals("bar", mav.getModel().get("foo"));
53+
}
54+
55+
@Test
56+
@DisplayName("Verify put(Map) adds all attributes to model and returns this")
57+
void testPutMultipleAttributes() {
58+
MapModelAndView mav = new MapModelAndView("index.html");
59+
60+
Map<String, Object> attributes = new HashMap<>();
61+
attributes.put("item1", 100);
62+
attributes.put("item2", "text");
63+
64+
MapModelAndView result = mav.put(attributes);
65+
66+
assertSame(mav, result, "put should return the current instance for fluent chaining");
67+
assertEquals(2, mav.getModel().size());
68+
assertEquals(100, mav.getModel().get("item1"));
69+
assertEquals("text", mav.getModel().get("item2"));
70+
}
71+
72+
@Test
73+
@DisplayName("Verify setLocale(Locale) delegates to super and returns this")
74+
void testSetLocale() {
75+
MapModelAndView mav = new MapModelAndView("index.html");
76+
Locale locale = Locale.UK;
77+
78+
MapModelAndView result = mav.setLocale(locale);
79+
80+
assertSame(mav, result, "setLocale should return the current instance for fluent chaining");
81+
assertEquals(locale, mav.getLocale());
82+
}
83+
}

0 commit comments

Comments
 (0)