{ "assetKey": "a-red-test-misread-as-a-product-defect-host-control", "kind": "terminal", "command": "d=$(mktemp -d)\ncat > $d/cp.gradle <<'GRADLE_EOF'\ngradle.projectsEvaluated {\n def p = rootProject.findProject(':adapter:outbound:httpclient')\n if (p != null) {\n p.tasks.register('printTestCp') {\n doLast { println 'TESTCP=' + p.sourceSets.test.runtimeClasspath.files.findAll { it.exists() }.join(':') }\n }\n }\n}\nGRADLE_EOF\ncat > $d/HostControl.java <<'JAVA_EOF'\nimport dev.caskeleton.adapter.outbound.httpclient.apache.ApacheBlockingTransportProvider;\nimport dev.caskeleton.adapter.outbound.httpclient.apache.ApacheFailureClassifier;\nimport dev.caskeleton.adapter.outbound.httpclient.api.operation.AttemptStage;\nimport dev.caskeleton.adapter.outbound.httpclient.profile.ClientProfile;\nimport dev.caskeleton.adapter.outbound.httpclient.profile.RuntimeGeneration;\nimport dev.caskeleton.adapter.outbound.httpclient.testkit.ClientProfiles;\nimport dev.caskeleton.adapter.outbound.httpclient.testkit.MockHttpServer;\nimport dev.caskeleton.adapter.outbound.httpclient.testkit.NoopLifecycleListener;\nimport dev.caskeleton.adapter.outbound.httpclient.testkit.TlsFixture;\nimport dev.caskeleton.adapter.outbound.httpclient.testkit.TlsMaterials;\nimport dev.caskeleton.adapter.outbound.httpclient.transport.TransportFailure;\nimport java.net.URI;\nimport java.util.Optional;\nimport org.springframework.web.client.RestClient;\n\npublic class HostControl {\n\n static String cut(String s) {\n if (s == null) return \"null\";\n s = s.replace('\\n', ' ');\n return s.length() > 96 ? s.substring(0, 96) + \"...\" : s;\n }\n\n static void run(String label, TlsFixture serverFixture, TlsFixture clientTrust, String host)\n throws Exception {\n try (MockHttpServer server = MockHttpServer.startTls(serverFixture.serverSocketFactory(), false)) {\n server.enqueueJson(200, \"{\\\"id\\\":1}\");\n URI base = server.uri(\"/\");\n URI root = new URI(base.getScheme(), null, host, base.getPort(), \"/\", null, null);\n URI target = new URI(base.getScheme(), null, host, base.getPort(), \"/users/1\", null, null);\n ClientProfile profile = ClientProfiles.builder(\"partner\").baseUrl(root).build();\n ApacheBlockingTransportProvider provider =\n new ApacheBlockingTransportProvider(\n Optional.empty(),\n ignored -> Optional.of(TlsMaterials.trustOnly(clientTrust)),\n ignored -> Optional.empty());\n System.out.println(\"=== \" + label + \" @\" + host + \" ===\");\n try {\n RestClient client =\n RestClient.builder()\n .requestFactory(\n provider.create(profile, new RuntimeGeneration(1), NoopLifecycleListener.INSTANCE))\n .build();\n try {\n client.get().uri(target).retrieve().body(String.class);\n System.out.println(\" 요청이 성공했다\");\n } catch (RuntimeException failure) {\n int i = 0;\n for (Throwable t = failure; t != null; t = t.getCause()) {\n System.out.println(\" [\" + (i++) + \"] \" + t.getClass().getSimpleName() + \" :: \" + cut(t.getMessage()));\n }\n TransportFailure c = new ApacheFailureClassifier().classify(failure, AttemptStage.TLS_HANDSHAKE);\n System.out.println(\" -> stage=\" + c.stage() + \" category=\" + c.category()\n + \" permanent=\" + c.category().permanent());\n }\n } finally {\n provider.close(profile, new RuntimeGeneration(1));\n }\n }\n }\n\n public static void main(String[] args) throws Exception {\n for (String host : new String[] {\"localhost\", \"127.0.0.1\"}) {\n // 테스트와 같은 픽스처 구성: 신뢰불가 CA 만 서버와 클라이언트가 서로 다른 신뢰재료를 쓴다.\n run(\"untrusted-authority\", TlsFixture.trusted(), TlsFixture.trusted(), host);\n TlsFixture expired = TlsFixture.expired();\n run(\"expired-certificate\", expired, expired, host);\n TlsFixture mismatch = TlsFixture.hostnameMismatch();\n run(\"hostname-mismatch\", mismatch, mismatch, host);\n }\n }\n}\nJAVA_EOF\nCP=$(./gradlew -I $d/cp.gradle :adapter:outbound:httpclient:printTestCp --offline --console=plain -q 2>/dev/null | grep '^TESTCP=' | head -1 | cut -d= -f2-)\njavac -nowarn -cp \"$CP\" -d $d $d/HostControl.java 2>&1 | head -5\necho '# 같은 서버·같은 신뢰재료로 접속 호스트만 바꾼다'\njava -cp \"$CP:$d\" HostControl 2>/dev/null\n", "cwd": "/shared/codebase/clean-architecture-backend-template/src", "exitCode": 0, "executedAt": "2026-09-02T01:51:16+00:00", "sourceRevision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916", "raw": "evidence/raw/a-red-test-misread-as-a-product-defect-host-control.txt", "svg": "evidence/rendered/a-red-test-misread-as-a-product-defect-host-control.svg", "rawSha256": "b3c6814158e193cd01f98d39fd3c7229c673f7a42088236fd37da6d5c9c43b6a", "lines": 32, "redaction": "none — 코드베이스 정적 검색" }