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>
125 lines
4.7 KiB
Markdown
125 lines
4.7 KiB
Markdown
---
|
|
kind: CASE
|
|
slug: analysis-finding-a18-f002
|
|
title: 실패는 환경 원인이며, 그 테스트의 도구 가드가 불완전하다
|
|
topic: composition-root-and-bootstrap
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: case:analysis-finding-a18-f002
|
|
evidenceCapturedOn: 2026-09-01
|
|
body: case-analysis-finding-a18-f002.body.md
|
|
assets:
|
|
- key: analysis-finding-a18-f002
|
|
file: ../../../final/evidence/rendered/analysis-finding-a18-f002.svg
|
|
evidence:
|
|
- ../../../final/evidence/raw/analysis-finding-a18-f002.txt
|
|
source:
|
|
- 원본 분석 절은 analysis/18-app-bootstrap.md#L549 이다.
|
|
---
|
|
|
|
# 실패는 환경 원인이며, 그 테스트의 도구 가드가 불완전하다
|
|
|
|
이 묶음의 유일한 실패는 저장소 결함이 아니다. 분석 환경에 도구 하나가 없고 위임된 스크립트가 그것을 요구하며 정직하게 실패한다. 기록하는 것은 같은 테스트가 첫째 도구는 건너뛰고 둘째 도구는 실패로 다룬다는 비대칭이다.
|
|
|
|
## 관계
|
|
|
|
- **아무것도 발견하지 못한 레인은 성공이 아니라 실패여야 한다**
|
|
이 저장소가 가진 반대 방향 원칙이다.
|
|
- **초록일 수 없는 게이트는 사람들이 건너뛰는 법을 배우게 만든다**
|
|
같은 계열의 규칙이다.
|
|
- **한 test 안에서 도구별로 갈리지 않는 편이 낫다**
|
|
권고의 형태다.
|
|
|
|
## 문제
|
|
|
|
부트스트랩 묶음을 돌렸다.
|
|
|
|
천열다섯 테스트가 통과하고 하나가 실패하고 넷이 건너뛰어졌다.
|
|
|
|
그 하나를 확인했다.
|
|
|
|
## 결론
|
|
|
|
저장소 결함이 아니다.
|
|
|
|
실패 메시지가 원인을 그대로 적는다. 위임된 스크립트가 도구 하나를 요구하고 없어서 종료 코드 칠십팔로 끝났다는 것이다.
|
|
|
|
분석 환경에 그 도구가 없다.
|
|
|
|
기록하는 것은 가드의 비대칭이다.
|
|
|
|
테스트는 컨테이너 도구 부재를 가정으로 처리해 건너뛴다.
|
|
|
|
같은 스크립트가 요구하는 다른 도구의 부재는 실패로 나타난다.
|
|
|
|
도구가 없는 기계에서 이 테스트는 계약 위반처럼 읽히는 실패를 낸다.
|
|
|
|
메시지가 원인을 드러내므로 오해가 오래가지는 않는다. 다만 이미 건너뛰기를 선택한 테스트가 두 번째 도구에 대해서만 다르게 행동한다.
|
|
|
|
이 저장소는 반대 방향의 원칙도 갖고 있다.
|
|
|
|
역방향 프록시 레인이 건너뛰기를 거부하며 그 이유를 적는다. 컨테이너 실행기가 없을 때 조용히 통과하는 레인은 그 실행기가 마지막으로 망가진 이래로 아무것도 인증하지 않은 레인이라는 것이다.
|
|
|
|
두 원칙 중 어느 쪽을 택하든 한 테스트 안에서 도구별로 갈리지는 않는 편이 낫다.
|
|
|
|
판정은 P3 다.
|
|
|
|
## 검증 환경
|
|
|
|
Gradle : 9.0.0
|
|
확인 방식 : 묶음 실행과 실패 메시지 확인, 도구 존재 확인
|
|
소스 수정 : x
|
|
|
|
## 재현 조건
|
|
|
|
원문은 final/evidence/raw/188 계열에 있다.
|
|
|
|
1. 부트스트랩 묶음을 실행한다.
|
|
2. 실패한 테스트와 메시지를 확인한다.
|
|
3. 위임된 스크립트가 요구하는 도구를 확인한다.
|
|
4. 그 도구가 환경에 있는지 확인한다.
|
|
5. 테스트의 가정 처리 대상을 확인한다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
테스트 실패의 형태는 다음이다.
|
|
|
|
```
|
|
org.opentest4j.AssertionFailedError: [verify-compose-profile-contracts.sh said:
|
|
jq is required
|
|
]
|
|
expected: 0 but was: 78
|
|
at ComposeMergeCharacterizationTest.everyLaneMatchesItsContract(ComposeMergeCharacterizationTest.java:62)
|
|
|
|
$ which jq -> NO_JQ
|
|
```
|
|
|
|
## 테스트 실패의 형태
|
|
|
|
:::evidence key="analysis-finding-a18-f002" alt="분석 문서 analysis/18-app-bootstrap.md 에서 이 기록의 근거 절을 그대로 잘라낸 15줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="analysis/18-app-bootstrap.md 발췌 — 15줄" zoom="true"
|
|
:::
|
|
|
|
## 기록하는 것은 가드의 비대칭이다
|
|
|
|
```java
|
|
@Test
|
|
void everyLaneMatchesItsContract() {
|
|
Assumptions.assumeTrue(dockerComposeIsAvailable(), "docker compose is not on this machine");
|
|
ProcessResult result = run(List.of("./scripts/verify-compose-profile-contracts.sh"));
|
|
assertThat(result.exitCode()).isZero();
|
|
}
|
|
```
|
|
|
|
docker compose는 가정으로 확인하고 `jq`는 확인하지 않는다. P3.
|
|
|
|
## 확인하지 못한 것
|
|
|
|
없는 도구를 설치하지 않았다. 분석 환경을 바꾸지 않는다는 원칙 때문이다. 계약 일치 자체는 독립 구현으로 따로 확인했다.
|
|
|
|
저장소 결함이 아니다. 분석 컨테이너에 jq가 없고, 위임된 스크립트가 그것을 요구하며 exit 78로 정직하게 실패한다.
|
|
|
|
<!-- body:end -->
|