From e955302fe8d6a370ddf5a874f99d537b287c2651 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 21 Apr 2026 21:24:54 +0200 Subject: [PATCH] fix(test): add required environmentId to agent register bodies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registration now requires environmentId in the body (400 if missing), so the stale register bodies were failing every downstream test that relied on a registered agent. Affected helpers in: - BootstrapTokenIT (static constant + inline body) - JwtRefreshIT (registerAndGetTokens) - RegistrationSecurityIT (registerAgent) - SseSigningIT (registerAgentWithAuth) - AgentSseControllerIT (registerAgent helper) Also in JwtRefreshIT / RegistrationSecurityIT, the "access token can reach a protected endpoint" tests were hitting env-scoped read endpoints that now require VIEWER+. Redirected both to the AGENT-role heartbeat endpoint — it proves the token is accepted by the security filter without being coupled to RBAC rules for reader endpoints. JwtRefreshIT.refreshWithValidToken also dropped an isNotEqualTo assertion that assumed sub-second iat uniqueness — HMAC JWTs with second-precision claims are byte-identical when minted for the same subject within the same second, so the old assertion was flaky by design. SseSigningIT / AgentSseControllerIT still have SSE-connection timing failures unrelated to registration — parked separately. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../app/controller/AgentSseControllerIT.java | 1 + .../server/app/security/BootstrapTokenIT.java | 2 ++ .../cameleer/server/app/security/JwtRefreshIT.java | 14 +++++++++----- .../app/security/RegistrationSecurityIT.java | 10 ++++++---- .../cameleer/server/app/security/SseSigningIT.java | 1 + 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java index 566a80ad..abf03741 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java @@ -57,6 +57,7 @@ class AgentSseControllerIT extends AbstractPostgresIT { { "instanceId": "%s", "applicationId": "%s", + "environmentId": "default", "version": "1.0.0", "routeIds": ["route-1"], "capabilities": {} diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenIT.java index e119dcba..237ab313 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenIT.java @@ -29,6 +29,7 @@ class BootstrapTokenIT extends AbstractPostgresIT { { "instanceId": "bootstrap-test-agent", "applicationId": "test-group", + "environmentId": "default", "version": "1.0.0", "routeIds": [], "capabilities": {} @@ -96,6 +97,7 @@ class BootstrapTokenIT extends AbstractPostgresIT { { "instanceId": "bootstrap-test-previous", "applicationId": "test-group", + "environmentId": "default", "version": "1.0.0", "routeIds": [], "capabilities": {} diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtRefreshIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtRefreshIT.java index c934cea8..8492e9c4 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtRefreshIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtRefreshIT.java @@ -39,6 +39,7 @@ class JwtRefreshIT extends AbstractPostgresIT { { "instanceId": "%s", "applicationId": "test-group", + "environmentId": "default", "version": "1.0.0", "routeIds": [], "capabilities": {} @@ -79,7 +80,9 @@ class JwtRefreshIT extends AbstractPostgresIT { JsonNode body = objectMapper.readTree(response.getBody()); assertThat(body.get("accessToken").asText()).isNotEmpty(); assertThat(body.get("refreshToken").asText()).isNotEmpty(); - assertThat(body.get("refreshToken").asText()).isNotEqualTo(refreshToken); + // NB: HMAC JWTs with second-precision iat/exp are byte-identical when + // minted for the same subject+claims within the same second, so we + // do not assert the new token differs from the old one. } @Test @@ -154,14 +157,15 @@ class JwtRefreshIT extends AbstractPostgresIT { JsonNode refreshBody2 = objectMapper.readTree(refreshResponse.getBody()); String newAccessToken = refreshBody2.get("accessToken").asText(); - // Use the new access token to hit a protected endpoint accessible by AGENT role + // Use the new access token to hit an AGENT-role endpoint (heartbeat) to + // verify the token is accepted by Spring Security. Env-scoped read + // endpoints now require VIEWER+, so an agent token would get 403 there. HttpHeaders authHeaders = new HttpHeaders(); authHeaders.set("Authorization", "Bearer " + newAccessToken); authHeaders.set("X-Cameleer-Protocol-Version", "1"); - ResponseEntity response = restTemplate.exchange( - "/api/v1/environments/default/executions", - HttpMethod.GET, + ResponseEntity response = restTemplate.postForEntity( + "/api/v1/agents/refresh-access-test/heartbeat", new HttpEntity<>(authHeaders), String.class); diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/security/RegistrationSecurityIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/security/RegistrationSecurityIT.java index fc6905c7..d85e0294 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/security/RegistrationSecurityIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/security/RegistrationSecurityIT.java @@ -32,6 +32,7 @@ class RegistrationSecurityIT extends AbstractPostgresIT { { "instanceId": "%s", "applicationId": "test-group", + "environmentId": "default", "version": "1.0.0", "routeIds": [], "capabilities": {} @@ -80,14 +81,15 @@ class RegistrationSecurityIT extends AbstractPostgresIT { JsonNode regBody = objectMapper.readTree(regResponse.getBody()); String accessToken = regBody.get("accessToken").asText(); - // Use the access token to hit a protected endpoint accessible by AGENT role + // Hit an AGENT-role endpoint (heartbeat) to verify the access token is + // accepted. Env-scoped read endpoints now require VIEWER+, so the agent + // token would get 403 there. HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Bearer " + accessToken); headers.set("X-Cameleer-Protocol-Version", "1"); - ResponseEntity response = restTemplate.exchange( - "/api/v1/environments/default/executions", - HttpMethod.GET, + ResponseEntity response = restTemplate.postForEntity( + "/api/v1/agents/reg-sec-access-test/heartbeat", new HttpEntity<>(headers), String.class); diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/security/SseSigningIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/security/SseSigningIT.java index cab8b9cc..35992e60 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/security/SseSigningIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/security/SseSigningIT.java @@ -90,6 +90,7 @@ class SseSigningIT extends AbstractPostgresIT { { "instanceId": "%s", "applicationId": "test-group", + "environmentId": "default", "version": "1.0.0", "routeIds": ["route-1"], "capabilities": {}