Files
document-haness/docs/clean-architecture-backend-template/final/evidence/meta/a-red-test-misread-as-a-product-defect-host-control.json
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

15 lines
5.0 KiB
JSON

{
"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 — 코드베이스 정적 검색"
}