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>
117 lines
4.8 KiB
Markdown
117 lines
4.8 KiB
Markdown
---
|
|
kind: CONCEPT
|
|
slug: strict-test-lane
|
|
title: strict test lane — 발견하지 못하면 실패하는 레인
|
|
topic: what-a-gate-does-not-prove
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: concept:strict-test-lane
|
|
evidenceCapturedOn: 2026-09-01
|
|
assets:
|
|
- key: strict-test-lane
|
|
file: ../../../final/evidence/rendered/strict-test-lane.svg
|
|
evidence:
|
|
- ../../../final/evidence/raw/strict-test-lane.txt
|
|
source:
|
|
- 원본 분석 절은 final/document.md#6-3 · analysis/05 §15.1 · analysis/19 §2.3 · analysis/20 §2.5 이다.
|
|
---
|
|
|
|
# strict test lane — 발견하지 못하면 실패하는 레인
|
|
|
|
태그로 선택되고 아무것도 발견하지 못하면 실패하며 업투데이트를 주장하지 않는 Test 태스크를, 각 리프가 복사하지 않고 의도만 선언하도록 만든 규약이다.
|
|
|
|
## 관계
|
|
|
|
- **아무것도 발견하지 못한 레인은 성공이 아니라 실패여야 한다**
|
|
이 규약이 강제하는 규칙이다.
|
|
- **빠뜨림이 통과가 되는 게이트는 게이트가 아니다**
|
|
같은 계열의 상위 규칙이다.
|
|
- **인증 레인만 Docker 가드를 달지 않는다**
|
|
이 규약 위에서 내려진 결정이다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
이 저장소가 "레인"을 정의하는 방식의 설명이다 — `failOnNoDiscoveredTests = true`(발견 0은 성공이 아니라 실패), `outputs.upToDateWhen { false }`(이전 실행 결과를 서빙하지 않음), 태그 기반 선택.
|
|
|
|
## failOnNoDiscoveredTests 만으로 부족한 이유
|
|
|
|
태그 필터가 discovery **이후**에 적용되므로, executed-selector 검사가 따로 있다.
|
|
|
|
## 레인을 정의하는 세 설정
|
|
|
|
:::evidence key="strict-test-lane" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true"
|
|
:::
|
|
|
|
## 두 결정이 공존하는 방식
|
|
|
|
Docker 부재를 skip이 아니라 에러로 두는 결정과, 반대로 랩톱 빌드를 깨지 않으려 `test`에서 태그를 제외하는 결정이 함께 있다.
|
|
|
|
:::note
|
|
|
|
플러그인이 check에 레인을 연결하지 않는다는 것은 확인했으나, TestKit 픽스처가 검증하는 범위는 읽지 않았다
|
|
|
|
:::
|
|
|
|
## 복사가 만든 문제
|
|
|
|
```groovy
|
|
// A strict test lane: a tagged, fail-closed Test task.
|
|
//
|
|
// Every lane in this repository repeated the same five lines — testClassesDirs, classpath,
|
|
// useJUnitPlatform { includeTags }, failOnNoDiscoveredTests, outputs.upToDateWhen { false } — once per
|
|
// lane, across five leaves. Copied machine code is not just noise: the two that mattered are
|
|
// `failOnNoDiscoveredTests` and `upToDateWhen { false }`, and a lane that is added by copy-paste is a
|
|
// lane that can silently lose either.
|
|
```
|
|
|
|
다섯 줄 중 둘이 중요하다. 나머지 셋은 잃어도 레인이 깨지므로 눈에 띈다. 그 둘은 잃어도 레인이 초록불로 남는다.
|
|
|
|
> A selected lane which discovers nothing then reports success
|
|
> for a thing nobody tested, and an up-to-date lane reports a result it did not produce.
|
|
|
|
## 의도만 선언한다
|
|
|
|
```groovy
|
|
strictTestLanes {
|
|
lane('mongoReplicaSetTest') {
|
|
tag = 'mongodb-replicaset'
|
|
description = 'Single-node replica set contract lane.'
|
|
customize = { test -> applyMongoImageSelection(test) }
|
|
}
|
|
}
|
|
```
|
|
|
|
리프가 쓰는 것은 이름과 태그와 설명뿐이다. 기계적인 부분은 규약이 갖는다.
|
|
|
|
`description`이 필수인 이유도 명시되어 있다 — 아무도 설명할 수 없는 레인은 아무도 해석할 수 없는 레인이다.
|
|
|
|
## 선택 방법은 정확히 셋 중 하나
|
|
|
|
레인은 태그로, 자기 소스셋으로, 또는 이름으로 지정한 정확한 테스트로 선택한다. 공유 `test` 소스셋 위에서 도는 레인이 셋 중 아무것도 쓰지 않으면 거부된다.
|
|
|
|
이유는 단순하다. 필터 없는 레인은 전체 suite 를 돌리면서 하나를 돌렸다고 주장하는 이름을 달게 된다.
|
|
|
|
## 빠져나갈 구멍이 없다
|
|
|
|
```groovy
|
|
// What a leaf may still not do is opt out of failing closed. There is no `failOnNoDiscoveredTests`
|
|
// knob on the DSL, deliberately.
|
|
```
|
|
|
|
:::tip
|
|
|
|
규약이 무엇을 제공하지 않는지가 규약의 내용이다. 옵트아웃 손잡이를 만들지 않으면 옵트아웃을 검토할 일도 없다.
|
|
|
|
:::
|
|
|
|
## 이 개념이 답하는 질문
|
|
|
|
레인이 초록불이라는 사실에서 무엇을 알 수 있는가. strict lane 이라면 최소한 두 가지를 안다. 테스트가 실제로 발견되어 실행됐다는 것, 그리고 그 결과가 이번 실행에서 나왔다는 것.
|
|
|
|
그 둘이 보장되지 않는 레인의 초록불은 아무것도 말하지 않는다.
|
|
|
|
<!-- body:end -->
|