Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/what-a-gate-does-not-prove/case/case-a-test-that-passed-on-the-wrong-guard.md
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

8.3 KiB

kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, assets, evidence, source
kind slug title topic project status sourceRevision rootTreeNode evidenceCapturedOn assets evidence source
CASE a-test-that-passed-on-the-wrong-guard 다중 타깃 검증을 확인한다는 테스트가 다른 가드에 걸려 통과했다 what-a-gate-does-not-prove clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 case:a-test-that-passed-on-the-wrong-guard 2026-09-02
key file
a-test-that-passed-on-the-wrong-guard ../../../final/evidence/rendered/a-test-that-passed-on-the-wrong-guard.svg
../../../final/evidence/raw/a-test-that-passed-on-the-wrong-guard.txt
분석 문서는 notification 어댑터 편 §9 다. 그 절이 같은 메서드가 전략을 두 번 본다는 것을 코드로 보이고, SINGLE 을 뺀 세 갈래를 도달 불가로 표시하며, 이름이 약속한 두 성질 중 하나만 그것도 두 번 확인된다고 적는다. 권고는 단언을 좁히는 것이다.

다중 타깃 검증을 확인한다는 테스트가 다른 가드에 걸려 통과했다

경로 형태 검증기의 첫 가드가 SINGLE 이 아닌 전략을 전부 거부한다. 그 뒤 switch 의 나머지 세 갈래와 폴백 그래프 검사는 도달할 수 없고, 그것을 확인한다는 테스트는 앞 가드의 예외로 통과한다.

관계

  • 빠뜨림이 통과가 되는 게이트는 게이트가 아니다 통과의 이유가 의도한 것과 다른 형태다.
  • 계약 테스트는 어댑터가 실제로 돌리는 statement를 실행해야 한다 계약 테스트가 어댑터의 실제 statement 를 돌려야 한다는 규칙이다.

문제

경로 형태 검증기 안에서 전략을 보는 자리가 한 메서드에 둘이다.

첫 번째는 가드다. 전략이 SINGLE 이 아니면 초기 카탈로그는 SINGLE 만 지원한다는 예외를 던진다.

두 번째는 switch 다. 네 갈래를 갖고, 그 뒤에 폴백 그래프 검사가 온다.

결론

첫 가드를 통과한 경로는 전략이 SINGLE 뿐이다. switch 의 나머지 세 갈래 — 팬아웃, 순서 폴백, 기본 — 에 들어갈 값이 없다. SINGLE 갈래가 폴백을 금지하므로 그 뒤 폴백 그래프 검사의 두 문구도 도달할 수 없다.

그것을 확인한다는 테스트가 있다. 이름은 폴백 범위와 순환이 거부된다고 말하고, 순환 경로와 팬아웃 경로를 각각 만든다. 단언 둘 다 타입을 확인한 뒤 메시지에서 전략이라는 단어를 찾는다.

두 경로 다 첫 가드에 걸린다. 그 문구에 그 단어가 있으므로 단언이 통과한다. 테스트가 실제로 확인하는 것은 SINGLE 이 아닌 전략이 거부된다는 성질 하나이고, 그것을 두 번 확인한다.

이름이 약속한 두 성질 중 하나는 확인되지 않는다.

첫 가드와 switch 사이에는 전략을 보지 않는 검사가 둘 있다. 타깃 수 일치와 증폭 한계다. 도달 불가를 만든 것은 위치가 아니라 switch 갈래 안에 들어갔다는 사실이다.

검증 환경

OpenJDK : 21.0.12 Gradle : 9.0.0 확인 방식 : 코드 경로와 테스트 단언 비교, 해당 테스트 실행 소스 수정 : x

재현 조건

  1. 경로 형태 검증기의 첫 가드와 그 뒤 전략을 보지 않는 검사 둘을 읽는다.
  2. switch 네 갈래와 그 뒤 폴백 그래프 검사가 던지는 두 문구를 확인한다.
  3. 테스트가 만든 두 라우트의 전략과 범위 값을 읽는다.
  4. 두 단언이 기대하는 문자열과 각 예외 문구를 대조한다.
  5. 이 두 갈래를 태울 라우트를 만드는 곳을 저장소 전체에서 센다.
  6. 그 테스트를 실행한다.

본문

경로 형태 검증기가 같은 메서드 안에서 전략을 두 번 본다. 앞은 가드이고 뒤는 switch 다.

앞의 가드가 뒤의 갈래들을 비운다

:::evidence key="a-test-that-passed-on-the-wrong-guard" alt="코드베이스에서 경로 형태 검증기의 첫 가드와 그 뒤 전략을 보지 않는 검사 둘, switch 네 갈래 전체와 기본 갈래의 문구, 폴백 그래프 검사가 던질 수 있는 두 문구의 위치, 순환을 거부한다는 테스트의 이름과 그 테스트가 만든 팬아웃 라우트와 순환 라우트의 전략과 범위 값과 두 단언, 이 두 분기를 태울 라우트를 만드는 곳의 저장소 전수 목록과 설정 파일의 비-SINGLE 전략 수, 그리고 그 테스트를 돌린 결과를 뽑은 출력 106줄. 순환 라우트의 범위가 그 갈래의 검사를 통과하는 값이라는 것이 그 출력에 보인다." caption="첫 가드와 전략을 보지 않는 검사 둘 · switch 네 갈래 · 폴백 그래프의 두 문구 · 두 라우트의 전략과 범위 · 전수 목록 · 5건 통과 — 106줄" zoom="true" :::

첫 가드는 전략이 SINGLE 이 아니면 예외를 던진다. 문구는 초기 카탈로그가 SINGLE 만 지원한다는 것이다.

그 뒤 switch 에 네 갈래가 있다. SINGLE 은 타깃 하나와 폴백 없음을 요구하고, 팬아웃은 폴백 정의를 금지하고, 순서 폴백은 타깃 둘 이상과 활성화 범위를 요구하고, 기본 갈래는 알 수 없는 전략을 거부한다.

가드를 통과한 값은 SINGLE 뿐이다. 나머지 세 갈래에 들어갈 값이 없다.

폴백 그래프 검사도 그 뒤에 있다

switch 가 끝나면 폴백 그래프 검사를 부른다. 던질 수 있는 문구가 둘이다. 폴백이 없는 타깃을 가리킬 때와, 순환일 때다.

SINGLE 갈래는 폴백이 하나라도 있으면 그 앞에서 거부한다. 그래서 이 검사에 도달하는 경로에는 폴백 간선이 없고, 두 문구 다 나올 수 없다.

테스트는 두 라우트를 만들고 같은 것을 묻는다

테스트 이름이 폴백 범위와 순환이 거부된다고 말한다.

하나는 팬아웃 라우트다. 다른 하나는 순서 폴백 라우트이고, 두 타깃이 서로를 폴백으로 가리키는 순환이다.

두 단언 모두 예외 타입과 메시지에 전략이라는 단어가 들어 있는지를 본다. 두 라우트 다 첫 가드에 걸리고, 그 문구에 그 단어가 있다.

그래서 이 테스트가 실제로 확인하는 것은 하나다. SINGLE 이 아닌 전략이 거부된다는 것. 그것을 두 번 확인한다.

가드가 사라지는 날 두 단언이 갈린다

순환 라우트는 타깃 둘과 폴백 활성화 하나를 갖는다. 순서 폴백 갈래의 범위 검사는 타깃 둘 미만이거나, 활성화가 하나 미만이거나, 활성화가 타깃 수보다 하나 이상 적지 않으면 거부한다. 이 값들은 셋 다 통과한다.

그러면 폴백 그래프 검사에 닿고 순환 문구를 던진다. 그 문구에는 전략이라는 단어가 없다. 이 단언은 그날 실패한다.

팬아웃 라우트는 다르다. 공급자 능력 카드의 전략이 라우트와 어긋나면 그것을 지목하는 예외가 나오는데, 그 문구에도 전략이라는 단어가 있다. 이 단언은 그날도 통과한다.

한쪽은 실패로 존재를 알리고, 다른 한쪽은 계속 조용하다.

이 갈래들을 태우는 라우트는 이 테스트뿐이다

저장소 전체에서 비 SINGLE 전략으로 라우트를 만드는 자바 코드를 찾으면 두 곳이다. 이 테스트의 두 라우트다.

같은 열거형을 쓰는 다른 파일들은 종류 정책 클래스와 그 테스트인데, 그 두 파일은 자기 클래스의 검증을 다룬다.

설정 파일에서 비 SINGLE 전략을 찾으면 0 이다.

도달 불가를 만든 것은 위치가 아니다

첫 가드와 switch 사이에도 검사가 둘 있다. 타깃 수가 상한과 정확히 같은지 보는 것과, 최악의 경우 호출 수를 계산해 총 한계와 비교하는 것이다.

둘 다 전략을 보지 않으므로 SINGLE 경로에서도 돈다. 갈라놓은 것은 switch 갈래 안에 들어갔는지 여부다.

고치는 방법

단언을 메시지 전문이나 그 갈래에만 있는 부분 문자열로 좁히면 된다. 순환이라는 단어나 잘못된 범위라는 문구가 그것이다.

지금 그 테스트를 돌리면 다섯 건이 통과한다.

확인하지 못한 것

첫 가드를 지운 사본을 만들어 돌려 보지는 않았다. 가드를 뺐을 때 각 라우트가 어디에 걸리는지는 실행 대신 범위 값과 갈래 조건으로 계산했다.