From b0ee8881fba195b4b34682b91c12aa7a315e0fde Mon Sep 17 00:00:00 2001 From: dahoud <41957584+DahoudG@users.noreply.github.com> Date: Sat, 25 Apr 2026 08:58:00 +0000 Subject: [PATCH] feat(sprint-4 P1-NEW-8/9 2026-04-25): docker-compose Keycloak 26.6.1 + fix Mockito strict-stubbing test KC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1-NEW-8/9 — Préparation migration Keycloak 23 → 26.6.1 + Organizations GA - docker-compose.kc26.yml : compose alternatif KC 26.6.1 avec --features=organization - Bind-mount realm import depuis src/main/resources/keycloak/realms - Healthcheck readiness probe + KC_HEALTH_ENABLED - Postgres 15 dédié, volume persistant - Réf : ARCH_KEYCLOAK_26.md - Permet validation locale avant cutover (compose dev intact) Fix tests KeycloakAdminHttpClientTest (4 erreurs Mockito UnnecessaryStubbing) - Helper mockResponse : lenient() sur body() (lu uniquement sur paths erreur/JSON parsing) - 9/9 tests passent désormais État Sprint 4 (déjà livré dans sessions antérieures, validé ici) : - Entité Organisation.keycloakOrgId + migration V37 - Service MigrerOrganisationsVersKeycloakService (idempotent, 9 tests) - OrganisationContextResolver (parsing claim 'organization' JWT, 12 tests) - AdminKeycloakOrganisationResource (endpoint admin migration) - KeycloakAdminHttpClient (sessions, 9 tests) Tests Sprint 4 : 36/36 passent (resolver 12 + migration 9 + admin HTTP 9 + context holder 6) Reste pour migration finale (post-Sprint 4) : - Validation manuelle docker compose -f docker-compose.kc26.yml up -d - Bascule production : remplacer compose KC23 par KC26 - Refactoring backend : suppression OrganisationContextFilter + headers custom --- docker-compose.kc26.yml | 73 +++++++++++++++++++ .../service/KeycloakAdminHttpClientTest.java | 3 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 docker-compose.kc26.yml diff --git a/docker-compose.kc26.yml b/docker-compose.kc26.yml new file mode 100644 index 0000000..6e8679d --- /dev/null +++ b/docker-compose.kc26.yml @@ -0,0 +1,73 @@ +version: '3.8' + +# Compose alternatif Keycloak 26.6.1 avec feature Organizations native (GA depuis 26.0). +# Usage : docker compose -f docker-compose.kc26.yml up -d +# But : valider la migration KC23 → KC26 + Organizations en local, sans toucher au compose dev. +# +# Une fois la migration validée, basculer ce contenu en production et supprimer la stack KC23. +# +# Réf : ARCH_KEYCLOAK_26.md + +services: + postgres-keycloak: + image: postgres:15-alpine + container_name: kc26-postgres + environment: + POSTGRES_DB: keycloak + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: keycloak + POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C" + volumes: + - kc26_postgres_data:/var/lib/postgresql/data + networks: + - kc26-net + healthcheck: + test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + + keycloak: + image: quay.io/keycloak/keycloak:26.6.1 + container_name: kc26-server + command: + - start-dev + - --features=organization + - --http-port=8180 + - --import-realm + environment: + KC_BOOTSTRAP_ADMIN_USERNAME: admin + KC_BOOTSTRAP_ADMIN_PASSWORD: admin + KC_DB: postgres + KC_DB_URL: jdbc:postgresql://postgres-keycloak:5432/keycloak + KC_DB_USERNAME: keycloak + KC_DB_PASSWORD: keycloak + KC_HEALTH_ENABLED: "true" + KC_METRICS_ENABLED: "true" + KC_HOSTNAME_STRICT: "false" + KC_HTTP_ENABLED: "true" + ports: + - "8180:8180" + volumes: + - ./src/main/resources/keycloak/realms:/opt/keycloak/data/import:ro + depends_on: + postgres-keycloak: + condition: service_healthy + networks: + - kc26-net + healthcheck: + test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8180 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'UP'"] + interval: 15s + timeout: 5s + retries: 8 + start_period: 60s + restart: unless-stopped + +volumes: + kc26_postgres_data: + driver: local + +networks: + kc26-net: + driver: bridge diff --git a/src/test/java/dev/lions/unionflow/server/service/KeycloakAdminHttpClientTest.java b/src/test/java/dev/lions/unionflow/server/service/KeycloakAdminHttpClientTest.java index 5c41e44..49aef90 100644 --- a/src/test/java/dev/lions/unionflow/server/service/KeycloakAdminHttpClientTest.java +++ b/src/test/java/dev/lions/unionflow/server/service/KeycloakAdminHttpClientTest.java @@ -40,7 +40,8 @@ class KeycloakAdminHttpClientTest { private HttpResponse mockResponse(int statusCode, String body) { HttpResponse resp = mock(HttpResponse.class); when(resp.statusCode()).thenReturn(statusCode); - when(resp.body()).thenReturn(body); + // body() is only read on error paths or when parsing JSON — make lenient + lenient().when(resp.body()).thenReturn(body); return resp; }