From d1d71e48ef315f7fee2cfc210b34af7ef8512d37 Mon Sep 17 00:00:00 2001 From: GeorgeC Date: Sat, 11 Jul 2026 10:54:18 -0400 Subject: [PATCH 1/2] feat(logging): adopt parent+BOM, Java 25 (stays Javalin; Jetty 11 BOM pin against Spring-BOM Jetty 12) Carved from pic_sure_api_mono_repo @ carve-source. Adds the module to the reactor, the logstash-logback-encoder BOM pin, and a reactor-aware path-filtered CI workflow. --- .github/workflows/pic-sure-logging-test.yml | 22 +++++++++ platform/pom.xml | 7 +++ pom.xml | 1 + services/pic-sure-logging/.sdkmanrc | 2 +- services/pic-sure-logging/jenkinsfile | 6 +-- services/pic-sure-logging/pom.xml | 49 +++++++++++---------- 6 files changed, 59 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/pic-sure-logging-test.yml diff --git a/.github/workflows/pic-sure-logging-test.yml b/.github/workflows/pic-sure-logging-test.yml new file mode 100644 index 000000000..64426d3a6 --- /dev/null +++ b/.github/workflows/pic-sure-logging-test.yml @@ -0,0 +1,22 @@ +name: pic-sure-logging tests +on: + pull_request: + paths: ['services/pic-sure-logging/**'] + push: + branches: [main] + paths: ['services/pic-sure-logging/**'] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 + - name: Set up JDK 25 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 #v5.2.0 + with: + java-version: '25' + distribution: 'temurin' + cache: maven + - name: Test with Maven + run: mvn -B -pl services/pic-sure-logging -am verify + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/platform/pom.xml b/platform/pom.xml index 6914e07c1..cd1c70c1d 100644 --- a/platform/pom.xml +++ b/platform/pom.xml @@ -28,6 +28,8 @@ 3.10.0 5.5.2 + + 8.1 @@ -92,6 +94,11 @@ httpclient5 ${httpclient5.version} + + net.logstash.logback + logstash-logback-encoder + ${logstash-logback-encoder.version} + diff --git a/pom.xml b/pom.xml index 6a48e4e58..1ebb36a89 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ services/picsure-dictionary services/picsure-dictionary/aggregate services/picsure-dictionary/dictionaryweights + services/pic-sure-logging diff --git a/services/pic-sure-logging/.sdkmanrc b/services/pic-sure-logging/.sdkmanrc index 38b733ba0..d2344323e 100644 --- a/services/pic-sure-logging/.sdkmanrc +++ b/services/pic-sure-logging/.sdkmanrc @@ -1 +1 @@ -java=21.0.10-tem +java=25.0.3-tem diff --git a/services/pic-sure-logging/jenkinsfile b/services/pic-sure-logging/jenkinsfile index e6d8a1e80..07d0104a5 100644 --- a/services/pic-sure-logging/jenkinsfile +++ b/services/pic-sure-logging/jenkinsfile @@ -40,11 +40,11 @@ pipeline { docker run --rm \ -v "$MOUNT_PATH/:/app:Z" \ -v maven_m2_cache:/root/.m2 \ - -w /app \ + -w /app/services/pic-sure-logging \ maven:3.9.9-amazoncorretto-24 \ sh -lc "mvn clean install -B -T 1C -DskipTests" - ls -lah target || true + ls -lah services/pic-sure-logging/target || true ''' } } @@ -62,7 +62,7 @@ pipeline { PROXY_ARGS="$PROXY_ARGS --build-arg no_proxy=$no_proxy --build-arg NO_PROXY=$no_proxy" fi - docker build --load $PROXY_ARGS -t "${IMAGE_NAME}:${IMAGE_TAG}" . + docker build --load $PROXY_ARGS -t "${IMAGE_NAME}:${IMAGE_TAG}" services/pic-sure-logging docker tag "${IMAGE_NAME}:${IMAGE_TAG}" "${IMAGE_NAME}:${LATEST_TAG}" ''' } diff --git a/services/pic-sure-logging/pom.xml b/services/pic-sure-logging/pom.xml index cf414e9d4..0a116638d 100644 --- a/services/pic-sure-logging/pom.xml +++ b/services/pic-sure-logging/pom.xml @@ -3,28 +3,40 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + + edu.harvard.hms.dbmi.avillach + pic-sure-api + ${revision} + ../../pom.xml + edu.harvard.dbmi.avillach pic-sure-logging - 1.0-SNAPSHOT jar PIC-SURE Audit Logging Service - 21 - 21 - UTF-8 + 6.7.0 - 2.17.2 4.4.0 - 2.0.17 - 1.5.29 - 8.1 - 5.10.3 - 5.14.2 + + + + + org.eclipse.jetty + jetty-bom + 11.0.25 + pom + import + + + + @@ -33,11 +45,10 @@ ${javalin.version} - + com.fasterxml.jackson.core jackson-databind - ${jackson.version} @@ -47,34 +58,29 @@ ${java-jwt.version} - + org.slf4j slf4j-api - ${slf4j.version} ch.qos.logback logback-classic - ${logback.version} net.logstash.logback logstash-logback-encoder - ${logstash-logback-encoder.version} - + org.junit.jupiter junit-jupiter - ${junit.version} test org.mockito mockito-core - ${mockito.version} test @@ -118,11 +124,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - 3.5.2 - From 454b21a4b8f2ce31b4a91cae3ed35fbc458f353c Mon Sep 17 00:00:00 2001 From: GeorgeC Date: Sat, 11 Jul 2026 10:55:42 -0400 Subject: [PATCH 2/2] style(logging): spotless-normalize (mechanical, repo formatter) --- .../harvard/dbmi/avillach/logging/App.java | 6 +- .../avillach/logging/config/AppConfig.java | 27 ++------- .../logging/handler/AuditHandler.java | 4 +- .../avillach/logging/handler/InfoHandler.java | 6 +- .../avillach/logging/model/AuditEvent.java | 11 +--- .../avillach/logging/model/RequestInfo.java | 21 +++---- .../avillach/logging/AppIntegrationTest.java | 59 ++++++------------- .../avillach/logging/HealthRouteTest.java | 12 ++-- .../avillach/logging/LogbackConfigTest.java | 56 +++++++----------- .../dbmi/avillach/logging/TestJwtBuilder.java | 3 +- .../logging/config/AppConfigTest.java | 15 ++--- .../logging/handler/AuditHandlerTest.java | 16 ++--- .../logging/service/AuditLogServiceTest.java | 14 ++--- .../logging/service/JwtDecodeServiceTest.java | 40 +++---------- 14 files changed, 88 insertions(+), 202 deletions(-) diff --git a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/App.java b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/App.java index 1c463fd9e..19f1343e8 100644 --- a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/App.java +++ b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/App.java @@ -31,8 +31,10 @@ public static void main(String[] args) { app.stop(); })); - log.info("Audit logging service started: app={}, platform={}, environment={}, hostname={}, port={}, allowedOrigin={}", - config.app(), config.platform(), config.environment(), config.hostname(), config.port(), config.allowedOrigin()); + log.info( + "Audit logging service started: app={}, platform={}, environment={}, hostname={}, port={}, allowedOrigin={}", config.app(), + config.platform(), config.environment(), config.hostname(), config.port(), config.allowedOrigin() + ); } public static Javalin createApp(AppConfig config, AtomicBoolean readiness) { diff --git a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/config/AppConfig.java b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/config/AppConfig.java index c3e01192a..b3555d5b8 100644 --- a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/config/AppConfig.java +++ b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/config/AppConfig.java @@ -8,31 +8,16 @@ import java.util.function.Function; public record AppConfig( - String auditApiKey, - String app, - String platform, - String environment, - String hostname, - int port, - String allowedOrigin, + String auditApiKey, String app, String platform, String environment, String hostname, int port, String allowedOrigin, Map jwtClaimMapping ) { private static final Map DEFAULT_JWT_CLAIM_MAPPING = Map.ofEntries( - Map.entry("sub", "subject"), - Map.entry("email", "user_email"), - Map.entry("name", "user_name"), - Map.entry("userid", "user_id"), - Map.entry("preferred_username", "preferred_username"), - Map.entry("org", "user_org"), - Map.entry("country_name", "user_country_name"), - Map.entry("nih_ico", "nih_ico"), - Map.entry("eRA_commons_id", "eRA_commons_id"), - Map.entry("user_permission_group", "user_permission_group"), - Map.entry("uuid", "uuid"), - Map.entry("roles", "roles"), - Map.entry("idp", "user_id_provider"), - Map.entry("cadr_name", "cadr_name") + Map.entry("sub", "subject"), Map.entry("email", "user_email"), Map.entry("name", "user_name"), Map.entry("userid", "user_id"), + Map.entry("preferred_username", "preferred_username"), Map.entry("org", "user_org"), Map.entry("country_name", "user_country_name"), + Map.entry("nih_ico", "nih_ico"), Map.entry("eRA_commons_id", "eRA_commons_id"), + Map.entry("user_permission_group", "user_permission_group"), Map.entry("uuid", "uuid"), Map.entry("roles", "roles"), + Map.entry("idp", "user_id_provider"), Map.entry("cadr_name", "cadr_name") ); public static AppConfig fromEnvironment() { diff --git a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/handler/AuditHandler.java b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/handler/AuditHandler.java index f6014c6e9..8c85cd90c 100644 --- a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/handler/AuditHandler.java +++ b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/handler/AuditHandler.java @@ -21,9 +21,7 @@ public AuditHandler(AuditLogService auditLogService) { this.auditLogService = auditLogService; this.objectMapper = new ObjectMapper(); objectMapper.getFactory().setStreamReadConstraints( - StreamReadConstraints.builder() - .maxNestingDepth(10) - .maxStringLength(10_240) // 10KB + StreamReadConstraints.builder().maxNestingDepth(10).maxStringLength(10_240) // 10KB .build() ); } diff --git a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/handler/InfoHandler.java b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/handler/InfoHandler.java index f99f06a4c..bbe818f78 100644 --- a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/handler/InfoHandler.java +++ b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/handler/InfoHandler.java @@ -11,11 +11,7 @@ public class InfoHandler { private final InfoResponse response; public InfoHandler() { - this.response = new InfoResponse( - UUID.nameUUIDFromBytes(":)".getBytes()), - "Logging Service", - List.of() - ); + this.response = new InfoResponse(UUID.nameUUIDFromBytes(":)".getBytes()), "Logging Service", List.of()); } public void handle(Context ctx) { diff --git a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/model/AuditEvent.java b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/model/AuditEvent.java index 91393f788..64b2507d8 100644 --- a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/model/AuditEvent.java +++ b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/model/AuditEvent.java @@ -7,13 +7,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record AuditEvent( - @JsonProperty("event_type") String eventType, - @JsonProperty("action") String action, - @JsonProperty("client_type") String clientType, - @JsonProperty("caller") String caller, - @JsonProperty("session_id") String sessionId, - @JsonProperty("request") RequestInfo request, - @JsonProperty("metadata") Map metadata, - @JsonProperty("error") Map error + @JsonProperty("event_type") String eventType, @JsonProperty("action") String action, @JsonProperty("client_type") String clientType, + @JsonProperty("caller") String caller, @JsonProperty("session_id") String sessionId, @JsonProperty("request") RequestInfo request, + @JsonProperty("metadata") Map metadata, @JsonProperty("error") Map error ) { } diff --git a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/model/RequestInfo.java b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/model/RequestInfo.java index cb170bc75..53e6a54ed 100644 --- a/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/model/RequestInfo.java +++ b/services/pic-sure-logging/src/main/java/edu/harvard/dbmi/avillach/logging/model/RequestInfo.java @@ -5,17 +5,10 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record RequestInfo( - @JsonProperty("request_id") String requestId, - @JsonProperty("method") String method, - @JsonProperty("url") String url, - @JsonProperty("query_string") String queryString, - @JsonProperty("src_ip") String srcIp, - @JsonProperty("dest_ip") String destIp, - @JsonProperty("dest_port") Integer destPort, - @JsonProperty("http_user_agent") String httpUserAgent, - @JsonProperty("http_content_type") String httpContentType, - @JsonProperty("status") Integer status, - @JsonProperty("bytes") Long bytes, - @JsonProperty("duration") Long duration, - @JsonProperty("referrer") String referrer -) {} + @JsonProperty("request_id") String requestId, @JsonProperty("method") String method, @JsonProperty("url") String url, + @JsonProperty("query_string") String queryString, @JsonProperty("src_ip") String srcIp, @JsonProperty("dest_ip") String destIp, + @JsonProperty("dest_port") Integer destPort, @JsonProperty("http_user_agent") String httpUserAgent, + @JsonProperty("http_content_type") String httpContentType, @JsonProperty("status") Integer status, @JsonProperty("bytes") Long bytes, + @JsonProperty("duration") Long duration, @JsonProperty("referrer") String referrer +) { +} diff --git a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/AppIntegrationTest.java b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/AppIntegrationTest.java index b4726bc09..26e8fd284 100644 --- a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/AppIntegrationTest.java +++ b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/AppIntegrationTest.java @@ -29,8 +29,7 @@ class AppIntegrationTest { private AppConfig createTestConfig() { return new AppConfig( - API_KEY, "testapp", "testplatform", "test", "testhost", - 0, "*", + API_KEY, "testapp", "testplatform", "test", "testhost", 0, "*", Map.of("sub", "subject", "email", "user_email", "roles", "roles", "logged_in", "logged_in") ); } @@ -53,11 +52,8 @@ void validRequestReturns202() { Javalin app = App.createApp(createTestConfig(), new AtomicBoolean(true)); JavalinTest.test(app, (server, client) -> { RequestBody body = RequestBody.create("{\"event_type\":\"QUERY\",\"action\":\"execute\"}", JSON); - Response response = client.request("/audit", builder -> - builder.post(body) - .header("X-API-Key", API_KEY) - .header("Content-Type", "application/json") - ); + Response response = client + .request("/audit", builder -> builder.post(body).header("X-API-Key", API_KEY).header("Content-Type", "application/json")); assertEquals(202, response.code()); assertTrue(response.body().string().contains("accepted")); @@ -70,10 +66,7 @@ void missingApiKeyReturns401() { Javalin app = App.createApp(createTestConfig(), new AtomicBoolean(true)); JavalinTest.test(app, (server, client) -> { RequestBody body = RequestBody.create("{\"event_type\":\"QUERY\"}", JSON); - Response response = client.request("/audit", builder -> - builder.post(body) - .header("Content-Type", "application/json") - ); + Response response = client.request("/audit", builder -> builder.post(body).header("Content-Type", "application/json")); assertEquals(401, response.code()); }); @@ -84,10 +77,8 @@ void wrongApiKeyReturns401() { Javalin app = App.createApp(createTestConfig(), new AtomicBoolean(true)); JavalinTest.test(app, (server, client) -> { RequestBody body = RequestBody.create("{\"event_type\":\"QUERY\"}", JSON); - Response response = client.request("/audit", builder -> - builder.post(body) - .header("X-API-Key", "wrong-key") - .header("Content-Type", "application/json") + Response response = client.request( + "/audit", builder -> builder.post(body).header("X-API-Key", "wrong-key").header("Content-Type", "application/json") ); assertEquals(401, response.code()); @@ -99,11 +90,8 @@ void badJsonReturns400() { Javalin app = App.createApp(createTestConfig(), new AtomicBoolean(true)); JavalinTest.test(app, (server, client) -> { RequestBody body = RequestBody.create("not-json", JSON); - Response response = client.request("/audit", builder -> - builder.post(body) - .header("X-API-Key", API_KEY) - .header("Content-Type", "application/json") - ); + Response response = client + .request("/audit", builder -> builder.post(body).header("X-API-Key", API_KEY).header("Content-Type", "application/json")); assertEquals(400, response.code()); }); @@ -114,11 +102,8 @@ void missingEventTypeReturns400() { Javalin app = App.createApp(createTestConfig(), new AtomicBoolean(true)); JavalinTest.test(app, (server, client) -> { RequestBody body = RequestBody.create("{\"action\":\"execute\"}", JSON); - Response response = client.request("/audit", builder -> - builder.post(body) - .header("X-API-Key", API_KEY) - .header("Content-Type", "application/json") - ); + Response response = client + .request("/audit", builder -> builder.post(body).header("X-API-Key", API_KEY).header("Content-Type", "application/json")); assertEquals(400, response.code()); }); @@ -139,16 +124,12 @@ void healthReturns200WithoutAuth() { void jwtClaimsAppearInLog() { Javalin app = App.createApp(createTestConfig(), new AtomicBoolean(true)); JavalinTest.test(app, (server, client) -> { - String token = TestJwtBuilder.buildToken(Map.of( - "sub", "user123", - "email", "user@example.com" - )); + String token = TestJwtBuilder.buildToken(Map.of("sub", "user123", "email", "user@example.com")); RequestBody body = RequestBody.create("{\"event_type\":\"QUERY\"}", JSON); - Response response = client.request("/audit", builder -> - builder.post(body) - .header("X-API-Key", API_KEY) - .header("Authorization", "Bearer " + token) + Response response = client.request( + "/audit", + builder -> builder.post(body).header("X-API-Key", API_KEY).header("Authorization", "Bearer " + token) .header("Content-Type", "application/json") ); @@ -165,10 +146,9 @@ void requestIdFromHeaderAppearsInLog() { Javalin app = App.createApp(createTestConfig(), new AtomicBoolean(true)); JavalinTest.test(app, (server, client) -> { RequestBody body = RequestBody.create("{\"event_type\":\"QUERY\"}", JSON); - Response response = client.request("/audit", builder -> - builder.post(body) - .header("X-API-Key", API_KEY) - .header("X-Request-Id", "req-abc-123") + Response response = client.request( + "/audit", + builder -> builder.post(body).header("X-API-Key", API_KEY).header("X-Request-Id", "req-abc-123") .header("Content-Type", "application/json") ); @@ -198,10 +178,7 @@ void infoReturns200WithoutAuth() { Javalin app = App.createApp(createTestConfig(), new AtomicBoolean(true)); JavalinTest.test(app, (server, client) -> { RequestBody body = RequestBody.create("{}", JSON); - Response response = client.request("/info", builder -> - builder.post(body) - .header("Content-Type", "application/json") - ); + Response response = client.request("/info", builder -> builder.post(body).header("Content-Type", "application/json")); assertEquals(200, response.code()); String responseBody = response.body().string(); diff --git a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/HealthRouteTest.java b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/HealthRouteTest.java index 990cbb7f3..74ef1b1fe 100644 --- a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/HealthRouteTest.java +++ b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/HealthRouteTest.java @@ -15,18 +15,16 @@ /** * Contract pin for the Javalin-native {@code GET /health} route (Phase 3 Task 8). * - *

The gateway's system-status aggregation probes this endpoint directly (this service is - * Javalin, not Spring Boot, so there is no Actuator {@code status:UP} body here). The contract - * being pinned is: {@code 200 {"status":"healthy"}} when ready, {@code 503 {"status":"starting"}} - * otherwise. This test exists so a future refactor of {@code App}/{@code HealthHandler} cannot - * silently change that contract without a test failing. + *

The gateway's system-status aggregation probes this endpoint directly (this service is Javalin, not Spring Boot, so there is no + * Actuator {@code status:UP} body here). The contract being pinned is: {@code 200 {"status":"healthy"}} when ready, {@code 503 + * {"status":"starting"}} otherwise. This test exists so a future refactor of {@code App}/{@code HealthHandler} cannot silently change that + * contract without a test failing. */ class HealthRouteTest { private static AppConfig testConfig() { return new AppConfig( - "test-api-key", "testapp", "testplatform", "test", "testhost", - 0, "*", + "test-api-key", "testapp", "testplatform", "test", "testhost", 0, "*", Map.of("sub", "subject", "email", "user_email", "roles", "roles", "logged_in", "logged_in") ); } diff --git a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/LogbackConfigTest.java b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/LogbackConfigTest.java index 573b1d710..0d315e9ab 100644 --- a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/LogbackConfigTest.java +++ b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/LogbackConfigTest.java @@ -46,21 +46,15 @@ void setUp() throws Exception { @Test void configurationLoadsWithoutErrors() { List statusList = context.getStatusManager().getCopyOfStatusList(); - List errors = statusList.stream() - .filter(s -> s.getLevel() == Status.ERROR) - .toList(); - assertTrue(errors.isEmpty(), - "Logback config should load without errors, but got: " + errors); + List errors = statusList.stream().filter(s -> s.getLevel() == Status.ERROR).toList(); + assertTrue(errors.isEmpty(), "Logback config should load without errors, but got: " + errors); } @Test void configurationLoadsWithoutWarnings() { List statusList = context.getStatusManager().getCopyOfStatusList(); - List warnings = statusList.stream() - .filter(s -> s.getLevel() == Status.WARN) - .toList(); - assertTrue(warnings.isEmpty(), - "Logback config should load without warnings, but got: " + warnings); + List warnings = statusList.stream().filter(s -> s.getLevel() == Status.WARN).toList(); + assertTrue(warnings.isEmpty(), "Logback config should load without warnings, but got: " + warnings); } // --- AUDIT logger appender tests --- @@ -86,8 +80,7 @@ void auditAsyncAppenderWrapsFileAppender() { void auditFileAppenderTargetsCorrectFile() { Logger auditLogger = context.getLogger("AUDIT"); AsyncAppender asyncAppender = findAppender(auditLogger, "ASYNC_AUDIT_FILE"); - RollingFileAppender fileAppender = - (RollingFileAppender) asyncAppender.getAppender("AUDIT_FILE"); + RollingFileAppender fileAppender = (RollingFileAppender) asyncAppender.getAppender("AUDIT_FILE"); assertEquals(tempDir.resolve("audit.log").toString(), fileAppender.getFile()); } @@ -95,8 +88,7 @@ void auditFileAppenderTargetsCorrectFile() { void auditFileAppenderUsesSizeAndTimeRollingPolicy() { Logger auditLogger = context.getLogger("AUDIT"); AsyncAppender asyncAppender = findAppender(auditLogger, "ASYNC_AUDIT_FILE"); - RollingFileAppender fileAppender = - (RollingFileAppender) asyncAppender.getAppender("AUDIT_FILE"); + RollingFileAppender fileAppender = (RollingFileAppender) asyncAppender.getAppender("AUDIT_FILE"); assertInstanceOf(SizeAndTimeBasedRollingPolicy.class, fileAppender.getRollingPolicy()); } @@ -104,8 +96,7 @@ void auditFileAppenderUsesSizeAndTimeRollingPolicy() { void auditFileAppenderHasCorrectMaxHistory() { Logger auditLogger = context.getLogger("AUDIT"); AsyncAppender asyncAppender = findAppender(auditLogger, "ASYNC_AUDIT_FILE"); - RollingFileAppender fileAppender = - (RollingFileAppender) asyncAppender.getAppender("AUDIT_FILE"); + RollingFileAppender fileAppender = (RollingFileAppender) asyncAppender.getAppender("AUDIT_FILE"); SizeAndTimeBasedRollingPolicy policy = (SizeAndTimeBasedRollingPolicy) fileAppender.getRollingPolicy(); assertEquals(30, policy.getMaxHistory()); } @@ -114,12 +105,10 @@ void auditFileAppenderHasCorrectMaxHistory() { void auditFileAppenderHasDateAndIndexInPattern() { Logger auditLogger = context.getLogger("AUDIT"); AsyncAppender asyncAppender = findAppender(auditLogger, "ASYNC_AUDIT_FILE"); - RollingFileAppender fileAppender = - (RollingFileAppender) asyncAppender.getAppender("AUDIT_FILE"); + RollingFileAppender fileAppender = (RollingFileAppender) asyncAppender.getAppender("AUDIT_FILE"); SizeAndTimeBasedRollingPolicy policy = (SizeAndTimeBasedRollingPolicy) fileAppender.getRollingPolicy(); String pattern = policy.getFileNamePattern(); - assertTrue(pattern.contains("audit.%d{yyyy-MM-dd}.%i.log"), - "Rolling pattern should include date and index for size+time rotation"); + assertTrue(pattern.contains("audit.%d{yyyy-MM-dd}.%i.log"), "Rolling pattern should include date and index for size+time rotation"); } // --- APP (root) logger appender tests --- @@ -144,8 +133,7 @@ void appAsyncAppenderWrapsFileAppender() { void appFileAppenderTargetsCorrectFile() { Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); AsyncAppender asyncAppender = findAppender(rootLogger, "ASYNC_APP_FILE"); - RollingFileAppender fileAppender = - (RollingFileAppender) asyncAppender.getAppender("APP_FILE"); + RollingFileAppender fileAppender = (RollingFileAppender) asyncAppender.getAppender("APP_FILE"); assertEquals(tempDir.resolve("app.log").toString(), fileAppender.getFile()); } @@ -153,8 +141,7 @@ void appFileAppenderTargetsCorrectFile() { void appFileAppenderUsesSizeAndTimeRollingPolicy() { Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); AsyncAppender asyncAppender = findAppender(rootLogger, "ASYNC_APP_FILE"); - RollingFileAppender fileAppender = - (RollingFileAppender) asyncAppender.getAppender("APP_FILE"); + RollingFileAppender fileAppender = (RollingFileAppender) asyncAppender.getAppender("APP_FILE"); assertInstanceOf(SizeAndTimeBasedRollingPolicy.class, fileAppender.getRollingPolicy()); } @@ -162,8 +149,7 @@ void appFileAppenderUsesSizeAndTimeRollingPolicy() { void appFileAppenderHasCorrectMaxHistory() { Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); AsyncAppender asyncAppender = findAppender(rootLogger, "ASYNC_APP_FILE"); - RollingFileAppender fileAppender = - (RollingFileAppender) asyncAppender.getAppender("APP_FILE"); + RollingFileAppender fileAppender = (RollingFileAppender) asyncAppender.getAppender("APP_FILE"); SizeAndTimeBasedRollingPolicy policy = (SizeAndTimeBasedRollingPolicy) fileAppender.getRollingPolicy(); assertEquals(30, policy.getMaxHistory()); } @@ -172,20 +158,17 @@ void appFileAppenderHasCorrectMaxHistory() { void appFileAppenderHasDateAndIndexInPattern() { Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); AsyncAppender asyncAppender = findAppender(rootLogger, "ASYNC_APP_FILE"); - RollingFileAppender fileAppender = - (RollingFileAppender) asyncAppender.getAppender("APP_FILE"); + RollingFileAppender fileAppender = (RollingFileAppender) asyncAppender.getAppender("APP_FILE"); SizeAndTimeBasedRollingPolicy policy = (SizeAndTimeBasedRollingPolicy) fileAppender.getRollingPolicy(); String pattern = policy.getFileNamePattern(); - assertTrue(pattern.contains("app.%d{yyyy-MM-dd}.%i.log"), - "Rolling pattern should include date and index for size+time rotation"); + assertTrue(pattern.contains("app.%d{yyyy-MM-dd}.%i.log"), "Rolling pattern should include date and index for size+time rotation"); } // --- Functional tests: logs actually get written to files --- @Test void rollingFileAppenderWritesToDisk() throws Exception { - LoggerContext defaultContext = - (LoggerContext) org.slf4j.LoggerFactory.getILoggerFactory(); + LoggerContext defaultContext = (LoggerContext) org.slf4j.LoggerFactory.getILoggerFactory(); RollingFileAppender appender = new RollingFileAppender<>(); appender.setContext(defaultContext); @@ -219,8 +202,10 @@ void rollingFileAppenderWritesToDisk() throws Exception { Path testLog = tempDir.resolve("rolling-test.log"); assertTrue(Files.exists(testLog), "rolling-test.log should be created"); String content = Files.readString(testLog); - assertTrue(content.contains("rolling file test event"), - "RollingFileAppender with SizeAndTimeBasedRollingPolicy should write events to disk"); + assertTrue( + content.contains("rolling file test event"), + "RollingFileAppender with SizeAndTimeBasedRollingPolicy should write events to disk" + ); } @Test @@ -237,8 +222,7 @@ void auditEventsDoNotAppearInAppLog() throws Exception { Path appLog = tempDir.resolve("app.log"); if (Files.exists(appLog)) { String content = Files.readString(appLog); - assertFalse(content.contains("secret audit data"), - "Audit events should not leak into app.log"); + assertFalse(content.contains("secret audit data"), "Audit events should not leak into app.log"); } } diff --git a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/TestJwtBuilder.java b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/TestJwtBuilder.java index 118d1ea65..bb5856637 100644 --- a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/TestJwtBuilder.java +++ b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/TestJwtBuilder.java @@ -35,8 +35,7 @@ public static String buildToken(Map claims) { } public static String buildExpiredToken(Map claims) { - var builder = JWT.create() - .withExpiresAt(Date.from(Instant.now().minusSeconds(3600))); + var builder = JWT.create().withExpiresAt(Date.from(Instant.now().minusSeconds(3600))); for (Map.Entry entry : claims.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); diff --git a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/config/AppConfigTest.java b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/config/AppConfigTest.java index 90f2df6e1..e61c4a662 100644 --- a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/config/AppConfigTest.java +++ b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/config/AppConfigTest.java @@ -41,8 +41,7 @@ void allVarsSet() { @Test void missingApiKeyFails() { Map env = new HashMap<>(); - IllegalStateException ex = assertThrows(IllegalStateException.class, - () -> AppConfig.fromEnvironment(envWith(env))); + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> AppConfig.fromEnvironment(envWith(env))); assertTrue(ex.getMessage().contains("LOGGING_API_KEY")); } @@ -50,8 +49,7 @@ void missingApiKeyFails() { void blankApiKeyFails() { Map env = new HashMap<>(); env.put("LOGGING_API_KEY", " "); - IllegalStateException ex = assertThrows(IllegalStateException.class, - () -> AppConfig.fromEnvironment(envWith(env))); + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> AppConfig.fromEnvironment(envWith(env))); assertTrue(ex.getMessage().contains("LOGGING_API_KEY")); } @@ -60,8 +58,7 @@ void invalidPortFails() { Map env = new HashMap<>(); env.put("LOGGING_API_KEY", "test-key"); env.put("PORT", "not-a-number"); - IllegalStateException ex = assertThrows(IllegalStateException.class, - () -> AppConfig.fromEnvironment(envWith(env))); + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> AppConfig.fromEnvironment(envWith(env))); assertTrue(ex.getMessage().contains("PORT")); } @@ -70,8 +67,7 @@ void portOutOfRangeFails() { Map env = new HashMap<>(); env.put("LOGGING_API_KEY", "test-key"); env.put("PORT", "99999"); - IllegalStateException ex = assertThrows(IllegalStateException.class, - () -> AppConfig.fromEnvironment(envWith(env))); + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> AppConfig.fromEnvironment(envWith(env))); assertTrue(ex.getMessage().contains("PORT")); } @@ -123,8 +119,7 @@ void invalidJwtClaimMappingFails() { env.put("LOGGING_API_KEY", "test-key"); env.put("JWT_CLAIM_MAPPING", "not-json"); - IllegalStateException ex = assertThrows(IllegalStateException.class, - () -> AppConfig.fromEnvironment(envWith(env))); + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> AppConfig.fromEnvironment(envWith(env))); assertTrue(ex.getMessage().contains("JWT_CLAIM_MAPPING")); } } diff --git a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/handler/AuditHandlerTest.java b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/handler/AuditHandlerTest.java index ab00dbc5f..726f5d5f9 100644 --- a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/handler/AuditHandlerTest.java +++ b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/handler/AuditHandlerTest.java @@ -116,9 +116,7 @@ void oversizedStringRejected() { @Test void metadataExceeding50KeysRejected() { - Map metadata = IntStream.rangeClosed(1, 51) - .boxed() - .collect(Collectors.toMap(i -> "key" + i, i -> "val" + i)); + Map metadata = IntStream.rangeClosed(1, 51).boxed().collect(Collectors.toMap(i -> "key" + i, i -> "val" + i)); String metadataJson = mapToJson(metadata); String json = "{\"event_type\":\"TEST\",\"metadata\":" + metadataJson + "}"; @@ -131,9 +129,7 @@ void metadataExceeding50KeysRejected() { @Test void errorExceeding20KeysRejected() { - Map error = IntStream.rangeClosed(1, 21) - .boxed() - .collect(Collectors.toMap(i -> "key" + i, i -> "val" + i)); + Map error = IntStream.rangeClosed(1, 21).boxed().collect(Collectors.toMap(i -> "key" + i, i -> "val" + i)); String errorJson = mapToJson(error); String json = "{\"event_type\":\"TEST\",\"error\":" + errorJson + "}"; @@ -146,9 +142,7 @@ void errorExceeding20KeysRejected() { @Test void metadataAtExactly50KeysAccepted() { - Map metadata = IntStream.rangeClosed(1, 50) - .boxed() - .collect(Collectors.toMap(i -> "key" + i, i -> "val" + i)); + Map metadata = IntStream.rangeClosed(1, 50).boxed().collect(Collectors.toMap(i -> "key" + i, i -> "val" + i)); String metadataJson = mapToJson(metadata); String json = "{\"event_type\":\"TEST\",\"metadata\":" + metadataJson + "}"; @@ -162,9 +156,7 @@ void metadataAtExactly50KeysAccepted() { @Test void errorAtExactly20KeysAccepted() { - Map error = IntStream.rangeClosed(1, 20) - .boxed() - .collect(Collectors.toMap(i -> "key" + i, i -> "val" + i)); + Map error = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toMap(i -> "key" + i, i -> "val" + i)); String errorJson = mapToJson(error); String json = "{\"event_type\":\"TEST\",\"error\":" + errorJson + "}"; diff --git a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/service/AuditLogServiceTest.java b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/service/AuditLogServiceTest.java index 7b3668164..212797caa 100644 --- a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/service/AuditLogServiceTest.java +++ b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/service/AuditLogServiceTest.java @@ -25,8 +25,7 @@ class AuditLogServiceTest { @BeforeEach void setUp() { AppConfig config = new AppConfig( - "test-key", "myapp", "myplatform", "staging", "myhost", - 8080, "*", + "test-key", "myapp", "myplatform", "staging", "myhost", 8080, "*", Map.of("sub", "subject", "email", "user_email", "roles", "roles", "logged_in", "logged_in") ); JwtDecodeService jwtService = new JwtDecodeService(config.jwtClaimMapping()); @@ -46,16 +45,11 @@ void tearDown() { @Test void fullEventWithAllFields() { RequestInfo request = new RequestInfo( - "req-123", "POST", "/api/query", "limit=10", - "192.168.1.1", "10.0.0.5", 8443, - "Mozilla/5.0", "application/json", 200, 1024L, 150L, - "https://example.com" + "req-123", "POST", "/api/query", "limit=10", "192.168.1.1", "10.0.0.5", 8443, "Mozilla/5.0", "application/json", 200, 1024L, + 150L, "https://example.com" ); AuditEvent event = new AuditEvent( - "QUERY", "execute", "web", null, null, - request, - Map.of("query_id", "q1"), - Map.of("code", "500", "message", "Internal error") + "QUERY", "execute", "web", null, null, request, Map.of("query_id", "q1"), Map.of("code", "500", "message", "Internal error") ); String token = TestJwtBuilder.buildToken(Map.of("sub", "user123", "email", "user@example.com")); diff --git a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/service/JwtDecodeServiceTest.java b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/service/JwtDecodeServiceTest.java index 169e75dd2..c6ec45b94 100644 --- a/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/service/JwtDecodeServiceTest.java +++ b/services/pic-sure-logging/src/test/java/edu/harvard/dbmi/avillach/logging/service/JwtDecodeServiceTest.java @@ -11,13 +11,8 @@ class JwtDecodeServiceTest { - private static final Map DEFAULT_MAPPING = Map.of( - "sub", "subject", - "email", "user_email", - "name", "user_name", - "roles", "roles", - "logged_in", "logged_in" - ); + private static final Map DEFAULT_MAPPING = + Map.of("sub", "subject", "email", "user_email", "name", "user_name", "roles", "roles", "logged_in", "logged_in"); private JwtDecodeService service; @@ -28,11 +23,7 @@ void setUp() { @Test void validTokenExtractsClaims() { - String token = TestJwtBuilder.buildToken(Map.of( - "sub", "user123", - "email", "user@example.com", - "name", "John Doe" - )); + String token = TestJwtBuilder.buildToken(Map.of("sub", "user123", "email", "user@example.com", "name", "John Doe")); Map result = service.extractClaims("Bearer " + token); @@ -86,9 +77,7 @@ void missingClaimsAreOmitted() { @Test void arrayRolesPreservedAsList() { - String token = TestJwtBuilder.buildToken(Map.of( - "roles", List.of("ADMIN", "USER") - )); + String token = TestJwtBuilder.buildToken(Map.of("roles", List.of("ADMIN", "USER"))); Map result = service.extractClaims("Bearer " + token); @@ -99,9 +88,7 @@ void arrayRolesPreservedAsList() { @Test void booleanLoggedInPreserved() { - String token = TestJwtBuilder.buildToken(Map.of( - "logged_in", true - )); + String token = TestJwtBuilder.buildToken(Map.of("logged_in", true)); Map result = service.extractClaims("Bearer " + token); assertEquals(true, result.get("logged_in")); @@ -109,10 +96,7 @@ void booleanLoggedInPreserved() { @Test void expiredTokenStillDecodes() { - String token = TestJwtBuilder.buildExpiredToken(Map.of( - "sub", "user123", - "email", "user@example.com" - )); + String token = TestJwtBuilder.buildExpiredToken(Map.of("sub", "user123", "email", "user@example.com")); Map result = service.extractClaims("Bearer " + token); assertEquals("user123", result.get("subject")); @@ -121,15 +105,9 @@ void expiredTokenStillDecodes() { @Test void customClaimMapping() { - JwtDecodeService customService = new JwtDecodeService(Map.of( - "custom_id", "my_id", - "custom_email", "my_email" - )); - - String token = TestJwtBuilder.buildToken(Map.of( - "custom_id", "abc", - "custom_email", "test@test.com" - )); + JwtDecodeService customService = new JwtDecodeService(Map.of("custom_id", "my_id", "custom_email", "my_email")); + + String token = TestJwtBuilder.buildToken(Map.of("custom_id", "abc", "custom_email", "test@test.com")); Map result = customService.extractClaims("Bearer " + token); assertEquals("abc", result.get("my_id"));