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>
97 lines
10 KiB
Plaintext
97 lines
10 KiB
Plaintext
Sub-scope 07 (notification) slack/webhook + email/google + testkit + template resources — manifest and probes
|
|
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
generatedAt=2026-08-30T07:01:25+00:00
|
|
|
|
=== OWNED FILES ===
|
|
8d8fe37be09d243fb97df633b23729fb30382956 main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailClient.java 19
|
|
88514a5f083ea33705176543ef257ba6f60c18af main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java 25
|
|
69e42abedab03da18483238b1618254292c0a80d main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailProvider.java 41
|
|
78e64152922eb35ce07a498a507adbe6b38e325d main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackClient.java 19
|
|
a09129dfab637aa5b32bd15c44eba03f2cc43ae8 main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java 25
|
|
b7805947e3e04e799712e6448bf822e8444c1418 main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackWebhookProvider.java 42
|
|
0bf316a4e6e68ca17bbff6cd5e53cb81b05ea91e test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/CallbackContract.java 96
|
|
6f4e1d597f0ccb31a015f582135286b7a78bde16 test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/ContractAdapters.java 274
|
|
346fc17c930bb3170dbc668f1a3f7347f01c4ad1 test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/CrossProviderContractSuiteTest.java 137
|
|
199d6ba156a53f6899cc1129db4b425a1f96d701 test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/NotificationChaosSecurityTest.java 124
|
|
cbcc07ee440a1ea6a8b64dd4766ae0ecb65e428b test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/NotificationPerformanceCertificationTest.java 141
|
|
76c5365f47107d10bf37e5c47dcb92cb1c16a82b test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/PiiLeakScanner.java 36
|
|
5c64b64a1b550ed32d64dba8827cb77e323909cf test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/ProviderAdapterContract.java 109
|
|
6a4e2bc808ea46db2b381bcd9e386121ea779229 test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/ProviderFaultHarness.java 125
|
|
8d1790dc425fc39d978cdcbe408293ca736cb20b test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/ProviderFixtures.java 127
|
|
c1000b004248ba20ad12fea292535bd604331434 test/resources/notification/templates/email/contract-v1.html 2
|
|
bea116c3fc2ce9008071ea1d49b486de0b6a5984 test/resources/notification/templates/email/contract-v1.subject.txt 1
|
|
756df6f304c23cf6a225d1184ae7dbe5032831a7 test/resources/notification/templates/email/contract-v1.text.txt 2
|
|
ce4b590959b647ef653dd931c78b1d2016b1ea89 test/resources/notification/templates/slack/contract-v1.txt 3
|
|
main_count=6 test_count=9 resource_count=4
|
|
|
|
=== 8.1 도달성: 공유 provider 계약을 상속하는 어댑터 테스트 ===
|
|
$ grep -rn 'extends ProviderAdapterContract' adapter/outbound/notification/src/test
|
|
adapter/outbound/notification/src/test/java/dev/caskeleton/adapter/outbound/notification/platform/provider/ses/SesNotificationProviderAdapterTest.java:50:class SesNotificationProviderAdapterTest extends ProviderAdapterContract {
|
|
adapter/outbound/notification/src/test/java/dev/caskeleton/adapter/outbound/notification/platform/provider/twilio/TwilioSmsProviderAdapterTest.java:24:class TwilioSmsProviderAdapterTest extends ProviderAdapterContract {
|
|
adapter/outbound/notification/src/test/java/dev/caskeleton/adapter/outbound/notification/platform/provider/apns/ApnsNotificationProviderAdapterTest.java:31:class ApnsNotificationProviderAdapterTest extends ProviderAdapterContract {
|
|
exit=0
|
|
|
|
$ grep -n 'new [A-Za-z]*ProviderAdapter\|new [A-Za-z]*SmsProviderAdapter' adapter/outbound/notification/src/test/java/dev/caskeleton/adapter/outbound/notification/platform/testkit/ContractAdapters.java
|
|
108: new SesNotificationProviderAdapter(
|
|
148: new TwilioSmsProviderAdapter(
|
|
175: new ApnsNotificationProviderAdapter(
|
|
199: new WebPushNotificationProviderAdapter(
|
|
236: new WebhookNotificationProviderAdapter(
|
|
exit=0
|
|
|
|
=== 8.2 조건 형제 비교: transport 실패가 ambiguous 로 번역되는 어댑터 ===
|
|
$ grep -rln 'ProviderResults.fromTransport' adapter/outbound/notification/src/main
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/provider/ses/SesNotificationProviderAdapter.java
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/provider/webpush/WebPushNotificationProviderAdapter.java
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/provider/twilio/TwilioSmsProviderAdapter.java
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/provider/apns/ApnsNotificationProviderAdapter.java
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/provider/webhook/WebhookNotificationProviderAdapter.java
|
|
exit=0
|
|
|
|
$ grep -n 'sendBatch\|try\|catch' adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/provider/fcm/FcmBatchCoordinator.java
|
|
83: FcmBatchResult batch = gateway.sendBatch(messages);
|
|
exit=0
|
|
|
|
$ grep -rln 'AMBIGUOUS' adapter/outbound/notification/src/test/java/dev/caskeleton/adapter/outbound/notification/platform/provider
|
|
adapter/outbound/notification/src/test/java/dev/caskeleton/adapter/outbound/notification/platform/provider/smtp/SmtpNotificationProviderAdapterTest.java
|
|
adapter/outbound/notification/src/test/java/dev/caskeleton/adapter/outbound/notification/platform/provider/webhook/WebhookNotificationProviderAdapterTest.java
|
|
exit=0
|
|
|
|
=== 8.3 SPI provider 두 종의 배선 ===
|
|
$ grep -n 'ConditionalOnProperty' -A 4 adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java:4:import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java-5-import org.springframework.context.annotation.Bean;
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java-6-import org.springframework.context.annotation.Configuration;
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java-7-
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java-8-/**
|
|
--
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java:18: @ConditionalOnProperty(
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java-19- name = "app.notification.slack-webhook.enabled",
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java-20- havingValue = "true",
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java-21- matchIfMissing = false)
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/slack/webhook/SlackNotificationAdapterConfig.java-22- public NotificationProvider slackWebhookProvider(SlackClient slackClient) {
|
|
--
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java:4:import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java-5-import org.springframework.context.annotation.Bean;
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java-6-import org.springframework.context.annotation.Configuration;
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java-7-
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java-8-/**
|
|
--
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java:18: @ConditionalOnProperty(
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java-19- name = "app.notification.google-email.enabled",
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java-20- havingValue = "true",
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java-21- matchIfMissing = false)
|
|
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/email/google/GoogleEmailNotificationAdapterConfig.java-22- public NotificationProvider googleEmailProvider(GoogleEmailClient googleEmailClient) {
|
|
exit=0
|
|
|
|
=== 8.4 테스트 레인 실행 ===
|
|
$ tail -6 /tmp/ntf_test.log
|
|
> Task :adapter:outbound:notification:test
|
|
|
|
BUILD SUCCESSFUL in 5s
|
|
19 actionable tasks: 1 executed, 18 up-to-date
|
|
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.0.0/userguide/configuration_cache_enabling.html
|
|
GRADLE_EXIT=0
|
|
exit=0
|
|
|