Skip to content

Commit 9f0db5b

Browse files
test(bpj): freeze public api contract
1 parent afd8771 commit 9f0db5b

1 file changed

Lines changed: 96 additions & 0 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package io.github.bpj;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.lang.reflect.Constructor;
7+
import java.lang.reflect.Method;
8+
import java.lang.reflect.Modifier;
9+
import java.util.Arrays;
10+
import java.util.Set;
11+
import java.util.TreeSet;
12+
import java.util.stream.Collectors;
13+
import org.junit.jupiter.api.Test;
14+
15+
class BPJPublicApiContractTest {
16+
17+
@Test
18+
void shouldKeepExpectedPublicApiForBpjClass() {
19+
assertTrue(Modifier.isFinal(BPJ.class.getModifiers()), "BPJ must remain final");
20+
21+
Constructor<?>[] constructors = BPJ.class.getDeclaredConstructors();
22+
assertEquals(1, constructors.length, "BPJ must keep a single private constructor");
23+
assertTrue(Modifier.isPrivate(constructors[0].getModifiers()), "BPJ constructor must stay private");
24+
25+
Set<String> expected = Set.of(
26+
"bind(java.lang.Object):io.github.bpj.BPJ$Scope",
27+
"bind(java.lang.Object[]):io.github.bpj.BPJ$Scope",
28+
"bind(java.util.Map):io.github.bpj.BPJ$Scope",
29+
"clearBoundContext():void",
30+
"format(java.lang.String):java.lang.String",
31+
"format(java.lang.String,java.lang.Object):java.lang.String",
32+
"format(java.lang.String,java.lang.Object[]):java.lang.String",
33+
"format(java.lang.String,java.util.Map):java.lang.String",
34+
"formatStrict(java.lang.String):java.lang.String",
35+
"formatStrict(java.lang.String,java.lang.Object):java.lang.String",
36+
"formatStrict(java.lang.String,java.lang.Object[]):java.lang.String",
37+
"formatStrict(java.lang.String,java.util.Map):java.lang.String",
38+
"hasBoundContext():boolean",
39+
"print(java.lang.String):void",
40+
"print(java.lang.String,java.lang.Object):void",
41+
"print(java.lang.String,java.lang.Object[]):void",
42+
"print(java.lang.String,java.util.Map):void",
43+
"println(java.lang.String):void",
44+
"println(java.lang.String,java.lang.Object):void",
45+
"println(java.lang.String,java.lang.Object[]):void",
46+
"println(java.lang.String,java.util.Map):void"
47+
);
48+
49+
Set<String> actual = Arrays.stream(BPJ.class.getDeclaredMethods())
50+
.filter(method -> Modifier.isPublic(method.getModifiers()))
51+
.filter(method -> !method.isSynthetic())
52+
.map(BPJPublicApiContractTest::signature)
53+
.collect(Collectors.toCollection(TreeSet::new));
54+
55+
assertEquals(new TreeSet<>(expected), actual, () -> mismatch("BPJ", expected, actual));
56+
}
57+
58+
@Test
59+
void shouldKeepExpectedScopeContract() {
60+
assertTrue(BPJ.Scope.class.isInterface(), "BPJ.Scope must remain an interface");
61+
62+
Set<String> expected = Set.of("close():void");
63+
64+
Set<String> actual = Arrays.stream(BPJ.Scope.class.getDeclaredMethods())
65+
.filter(method -> Modifier.isPublic(method.getModifiers()))
66+
.filter(method -> !method.isSynthetic())
67+
.map(BPJPublicApiContractTest::signature)
68+
.collect(Collectors.toCollection(TreeSet::new));
69+
70+
assertEquals(new TreeSet<>(expected), actual, () -> mismatch("BPJ.Scope", expected, actual));
71+
}
72+
73+
private static String signature(Method method) {
74+
String params = Arrays.stream(method.getParameterTypes())
75+
.map(BPJPublicApiContractTest::typeName)
76+
.collect(Collectors.joining(","));
77+
return method.getName() + "(" + params + "):" + typeName(method.getReturnType());
78+
}
79+
80+
private static String typeName(Class<?> type) {
81+
if (type.isArray()) {
82+
return typeName(type.getComponentType()) + "[]";
83+
}
84+
return type.getName();
85+
}
86+
87+
private static String mismatch(String owner, Set<String> expected, Set<String> actual) {
88+
Set<String> missing = new TreeSet<>(expected);
89+
missing.removeAll(actual);
90+
91+
Set<String> added = new TreeSet<>(actual);
92+
added.removeAll(expected);
93+
94+
return owner + " API changed. Missing: " + missing + " | Added: " + added;
95+
}
96+
}

0 commit comments

Comments
 (0)