Skip to content

Commit af28605

Browse files
committed
test(coverage): restore JaCoCo 0.90 bundle gate
Add deterministic regression coverage for the RFC 2606 reserved-TLD guards, EndpointPolicy edge branches (malformed URL, blank host, default port, trusted factories, fake-resolver strict-public paths) and HermesClient profile URL helpers. No production code changes.
1 parent d3c6431 commit af28605

4 files changed

Lines changed: 218 additions & 0 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.github.easy4j.hermes;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertThrows;
7+
8+
/**
9+
* HermesClient profile URL 拼接辅助的回归测试(包级静态方法直接覆盖)。
10+
*/
11+
class HermesClientProfileUrlTest {
12+
13+
@Test
14+
void shouldAppendProfileSegment() {
15+
assertEquals("http://127.0.0.1:8642/p/team-a",
16+
HermesClient.profileServerUrl("http://127.0.0.1:8642/", "team-a"));
17+
assertEquals("http://127.0.0.1:8642/p/team-a",
18+
HermesClient.profileServerUrl("http://127.0.0.1:8642", " team-a "));
19+
}
20+
21+
@Test
22+
void shouldRejectBlankServerUrl() {
23+
assertThrows(IllegalStateException.class, () -> HermesClient.profileServerUrl(" ", "team-a"));
24+
assertThrows(IllegalStateException.class, () -> HermesClient.profileServerUrl(null, "team-a"));
25+
}
26+
27+
@Test
28+
void shouldRejectIllegalProfileIdInsideUrl() {
29+
assertThrows(IllegalArgumentException.class,
30+
() -> HermesClient.profileServerUrl("http://127.0.0.1:8642", "../evil"));
31+
}
32+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package io.github.easy4j.hermes.security;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.net.InetAddress;
6+
import java.net.UnknownHostException;
7+
8+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
10+
import static org.junit.jupiter.api.Assertions.assertTrue;
11+
12+
/**
13+
* EndpointPolicy 边界分支的确定性覆盖测试。
14+
*
15+
* <p>全部用例不依赖真实 DNS:要么在解析前即被拦截(畸形 URL、空 host、端口失配),
16+
* 要么通过包级 HostResolver 注入固定解析结果(仅 strictPublic 提供注入点)。</p>
17+
*/
18+
class EndpointPolicyEdgeCoverageTest {
19+
20+
private static InetAddress addr(String literal) {
21+
try {
22+
return InetAddress.getByName(literal);
23+
} catch (UnknownHostException e) {
24+
throw new IllegalStateException(e);
25+
}
26+
}
27+
28+
@Test
29+
void shouldRejectMalformedUrlBeforeSchemeCheck() {
30+
EndpointPolicy policy = EndpointPolicy.strictPublic();
31+
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
32+
() -> policy.require("https://[::1"));
33+
assertTrue(ex.getMessage().contains("Malformed URL"));
34+
}
35+
36+
@Test
37+
void shouldRejectUrlWithoutHost() {
38+
EndpointPolicy policy = EndpointPolicy.strictPublic();
39+
assertThrows(IllegalArgumentException.class, () -> policy.require("https:///path"));
40+
}
41+
42+
@Test
43+
void shouldTreatPortlessHttpsAsPort443AndRejectOriginMismatch() {
44+
EndpointPolicy policy = EndpointPolicy.trustedLocal("127.0.0.1", 8642);
45+
assertThrows(IllegalArgumentException.class, () -> policy.require("https://127.0.0.1"));
46+
}
47+
48+
@Test
49+
void shouldRejectInvalidTrustedPort() {
50+
assertThrows(IllegalArgumentException.class, () -> EndpointPolicy.trustedLocal("127.0.0.1", 0));
51+
assertThrows(IllegalArgumentException.class,
52+
() -> EndpointPolicy.trustedPrivate("10.0.0.5", 70000));
53+
}
54+
55+
@Test
56+
void shouldRejectLoopbackTrustedPrivateHostAtFactoryTime() {
57+
// 127.0.0.1 是字面量,SYSTEM_RESOLVER 不经过网络;工厂阶段即拒绝环回地址。
58+
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
59+
() -> EndpointPolicy.trustedPrivate("127.0.0.1", 8643));
60+
assertTrue(ex.getMessage().contains("private addresses"));
61+
}
62+
63+
@Test
64+
void shouldAcceptSiteLocalInsideTrustedPrivate() {
65+
EndpointPolicy policy = EndpointPolicy.trustedPrivate("192.168.1.10", 8644);
66+
assertDoesNotThrow(() -> policy.require("http://192.168.1.10:8644"));
67+
}
68+
69+
@Test
70+
void strictPublicShouldRejectEmptyResolution() {
71+
EndpointPolicy policy = EndpointPolicy.strictPublic(host -> new InetAddress[0]);
72+
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
73+
() -> policy.require("https://multi-nic.gateway.example.com"));
74+
assertTrue(ex.getMessage().contains("no addresses"));
75+
}
76+
77+
@Test
78+
void strictPublicShouldRejectWhenAnyResolvedAddressIsPrivate() {
79+
EndpointPolicy policy = EndpointPolicy.strictPublic(
80+
host -> new InetAddress[] {addr("93.184.216.34"), addr("10.0.0.7")});
81+
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
82+
() -> policy.require("https://multi-nic.gateway.example.com"));
83+
assertTrue(ex.getMessage().contains("non-public address"));
84+
assertTrue(ex.getMessage().contains("10.0.0.7"));
85+
}
86+
87+
@Test
88+
void strictPublicShouldRejectUnresolvableHost() {
89+
EndpointPolicy policy = EndpointPolicy.strictPublic(host -> {
90+
throw new UnknownHostException("injected");
91+
});
92+
assertThrows(IllegalArgumentException.class,
93+
() -> policy.require("https://injected-failure.gateway.example.com"));
94+
}
95+
96+
@Test
97+
void strictPublicShouldRejectLoopbackResolution() {
98+
EndpointPolicy policy = EndpointPolicy.strictPublic(
99+
host -> new InetAddress[] {addr("127.0.0.1")});
100+
assertThrows(IllegalArgumentException.class,
101+
() -> policy.require("https://rebind.gateway.example.com"));
102+
}
103+
104+
@Test
105+
void strictPublicShouldAcceptPublicIpv6AndStripBrackets() {
106+
EndpointPolicy policy = EndpointPolicy.strictPublic(
107+
host -> new InetAddress[] {addr("2001:4860:4860::8888")});
108+
assertDoesNotThrow(() -> policy.require("https://[2001:4860:4860::8888]/v1"));
109+
}
110+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.github.easy4j.hermes.security;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
/**
9+
* EndpointPolicy 层的 RFC 2606 保留 TLD 确定性拦截回归测试。
10+
*
11+
* <p>对应 Java: EndpointPolicy#rejectReservedTld —— strictPublic 的
12+
* require 必须在 DNS 解析之前拒绝保留 TLD,避免通配 DNS 环境绕过。</p>
13+
*/
14+
class ReservedTldPolicyCoverageTest {
15+
16+
@Test
17+
void strictPublicShouldRejectEveryReservedTldBeforeDns() {
18+
EndpointPolicy policy = EndpointPolicy.strictPublic();
19+
for (String host : new String[] {
20+
"gateway.invalid", "gateway.test", "gateway.example",
21+
"gateway.localhost", "localhost"}) {
22+
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
23+
() -> policy.require("https://" + host + "/v1"));
24+
assertTrue(ex.getMessage().contains("reserved test host"),
25+
"expected reserved-TLD rejection for " + host + " but got: " + ex.getMessage());
26+
}
27+
}
28+
29+
@Test
30+
void trustedLocalShouldAlsoRejectReservedTld() {
31+
EndpointPolicy policy = EndpointPolicy.trustedLocal("127.0.0.1", 8642);
32+
assertThrows(IllegalArgumentException.class,
33+
() -> policy.require("https://sub.localhost/v1"));
34+
}
35+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.github.easy4j.hermes.util;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
6+
import static org.junit.jupiter.api.Assertions.assertThrows;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
9+
/**
10+
* RFC 2606 保留 TLD(.invalid/.test/.example/.localhost)确定性拦截的守卫层回归测试。
11+
*
12+
* <p>通配 DNS 环境可能把保留 TLD 解析到公网 IP,绕过基于解析结果的防护;
13+
* EndpointGuard 必须在 DNS 之前按后缀确定性拒绝。</p>
14+
*/
15+
class ReservedTldGuardCoverageTest {
16+
17+
@Test
18+
void shouldRejectReservedTldHostsBeforeDns() {
19+
for (String host : new String[] {
20+
"hermes.invalid", "hermes.test", "hermes.example",
21+
"hermes.localhost", "localhost"}) {
22+
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
23+
() -> EndpointGuard.requireSafeHost(host, "https://" + host + "/v1"));
24+
assertTrue(ex.getMessage().contains("reserved test host"),
25+
"expected reserved-TLD rejection for " + host + " but got: " + ex.getMessage());
26+
}
27+
}
28+
29+
@Test
30+
void shouldRejectReservedTldUrls() {
31+
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
32+
() -> EndpointGuard.require("https://gateway.hermes.invalid/v1/chat"));
33+
assertTrue(ex.getMessage().contains("reserved test host"));
34+
}
35+
36+
@Test
37+
void shouldStillAcceptPublicIpLiterals() {
38+
// 公网 IP 字面量不经过 DNS,也不应触发保留 TLD 分支。
39+
assertDoesNotThrow(() -> EndpointGuard.requireSafeHost("93.184.216.34", "https://93.184.216.34/v1"));
40+
}
41+
}

0 commit comments

Comments
 (0)