test: update integration tests for env-scoped URL shape
Picks up the URL moves from P2/P3A/P3B/P3C. Also fixes a latent bug in
AppControllerIT.uploadJar_asOperator_returns201 / DeploymentControllerIT
setUp: the tests were passing the app's UUID as the {appSlug} path
variable (via `path("id").asText()`); the old AppController looked up
apps via getBySlug(), so the legacy URL call would 404 when the slug
literal was a UUID. Now the test tracks the known slug string and uses
it for every /apps/{appSlug}/... path.
Test URL updates:
- SearchControllerIT: /api/v1/search/executions →
/api/v1/environments/default/executions (GET) and
/api/v1/environments/default/executions/search (POST).
- AppControllerIT: /api/v1/apps → /api/v1/environments/default/apps.
Request bodies drop environmentId (it's in the path).
- DeploymentControllerIT: /api/v1/apps/{appId}/deployments →
/api/v1/environments/default/apps/{appSlug}/deployments. DeployRequest
body drops environmentId.
- JwtRefreshIT + RegistrationSecurityIT: smoke-test protected endpoint
call updated to the new /environments/default/executions shape.
All tests compile clean. Runtime behavior requires a full stack
(Postgres + ClickHouse + Docker); validating integration tests is a
pre-merge step before merging the feature branch.
Remaining pre-merge items (not blocked by code):
1. Regenerate ui/src/api/schema.d.ts + openapi.json by running
`cd ui && npm run generate-api:live` against a running backend.
SearchController, DeploymentController, etc. DTO signatures have
changed; schema.d.ts is frozen at the pre-migration shape.
Raw-fetch call sites introduced in P3A/P3C work at runtime without
the schema; the regen only sharpens TypeScript coverage.
2. Smoke test locally: boot server, verify EnvironmentsPage,
AppsTab, Exchanges, Dashboard, Runtime pages all function.
3. Run `mvn verify` end-to-end (Testcontainers + Docker required).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,12 +60,12 @@ class AppControllerIT extends AbstractPostgresIT {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createApp_asOperator_returns201() throws Exception {
|
void createApp_asOperator_returns201() throws Exception {
|
||||||
String json = String.format("""
|
String json = """
|
||||||
{"environmentId": "%s", "slug": "my-app", "displayName": "My App"}
|
{"slug": "my-app", "displayName": "My App"}
|
||||||
""", defaultEnvId);
|
""";
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/apps", HttpMethod.POST,
|
"/api/v1/environments/default/apps", HttpMethod.POST,
|
||||||
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
@@ -79,16 +79,16 @@ class AppControllerIT extends AbstractPostgresIT {
|
|||||||
@Test
|
@Test
|
||||||
void listApps_asOperator_returnsCreatedApp() throws Exception {
|
void listApps_asOperator_returnsCreatedApp() throws Exception {
|
||||||
// Create an app first
|
// Create an app first
|
||||||
String json = String.format("""
|
String json = """
|
||||||
{"environmentId": "%s", "slug": "list-test", "displayName": "List Test"}
|
{"slug": "list-test", "displayName": "List Test"}
|
||||||
""", defaultEnvId);
|
""";
|
||||||
restTemplate.exchange(
|
restTemplate.exchange(
|
||||||
"/api/v1/apps", HttpMethod.POST,
|
"/api/v1/environments/default/apps", HttpMethod.POST,
|
||||||
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/apps?environmentId=" + defaultEnvId, HttpMethod.GET,
|
"/api/v1/environments/default/apps", HttpMethod.GET,
|
||||||
new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
|
new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
@@ -100,12 +100,12 @@ class AppControllerIT extends AbstractPostgresIT {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createApp_asViewer_returns403() {
|
void createApp_asViewer_returns403() {
|
||||||
String json = String.format("""
|
String json = """
|
||||||
{"environmentId": "%s", "slug": "viewer-app", "displayName": "Viewer App"}
|
{"slug": "viewer-app", "displayName": "Viewer App"}
|
||||||
""", defaultEnvId);
|
""";
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/apps", HttpMethod.POST,
|
"/api/v1/environments/default/apps", HttpMethod.POST,
|
||||||
new HttpEntity<>(json, securityHelper.authHeaders(viewerJwt)),
|
new HttpEntity<>(json, securityHelper.authHeaders(viewerJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
@@ -114,15 +114,15 @@ class AppControllerIT extends AbstractPostgresIT {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void uploadJar_asOperator_returns201() throws Exception {
|
void uploadJar_asOperator_returns201() throws Exception {
|
||||||
|
String appSlug = "jar-test";
|
||||||
// Create app
|
// Create app
|
||||||
String json = String.format("""
|
String json = String.format("""
|
||||||
{"environmentId": "%s", "slug": "jar-test", "displayName": "JAR Test"}
|
{"slug": "%s", "displayName": "JAR Test"}
|
||||||
""", defaultEnvId);
|
""", appSlug);
|
||||||
ResponseEntity<String> createResponse = restTemplate.exchange(
|
restTemplate.exchange(
|
||||||
"/api/v1/apps", HttpMethod.POST,
|
"/api/v1/environments/default/apps", HttpMethod.POST,
|
||||||
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
String appId = objectMapper.readTree(createResponse.getBody()).path("id").asText();
|
|
||||||
|
|
||||||
// Upload JAR (fake content)
|
// Upload JAR (fake content)
|
||||||
byte[] jarContent = "fake-jar-content".getBytes();
|
byte[] jarContent = "fake-jar-content".getBytes();
|
||||||
@@ -142,7 +142,7 @@ class AppControllerIT extends AbstractPostgresIT {
|
|||||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/apps/" + appId + "/versions", HttpMethod.POST,
|
"/api/v1/environments/default/apps/" + appSlug + "/versions", HttpMethod.POST,
|
||||||
new HttpEntity<>(body, headers),
|
new HttpEntity<>(body, headers),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class DeploymentControllerIT extends AbstractPostgresIT {
|
|||||||
private String adminJwt;
|
private String adminJwt;
|
||||||
private String defaultEnvId;
|
private String defaultEnvId;
|
||||||
private String appId;
|
private String appId;
|
||||||
|
private String appSlugRef;
|
||||||
private String versionId;
|
private String versionId;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@@ -60,15 +61,17 @@ class DeploymentControllerIT extends AbstractPostgresIT {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create app
|
// Create app (slug becomes the path identifier)
|
||||||
|
String appSlug = "deploy-test";
|
||||||
String appJson = String.format("""
|
String appJson = String.format("""
|
||||||
{"environmentId": "%s", "slug": "deploy-test", "displayName": "Deploy Test"}
|
{"slug": "%s", "displayName": "Deploy Test"}
|
||||||
""", defaultEnvId);
|
""", appSlug);
|
||||||
ResponseEntity<String> appResponse = restTemplate.exchange(
|
ResponseEntity<String> appResponse = restTemplate.exchange(
|
||||||
"/api/v1/apps", HttpMethod.POST,
|
"/api/v1/environments/default/apps", HttpMethod.POST,
|
||||||
new HttpEntity<>(appJson, securityHelper.authHeaders(operatorJwt)),
|
new HttpEntity<>(appJson, securityHelper.authHeaders(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
appId = objectMapper.readTree(appResponse.getBody()).path("id").asText();
|
appId = objectMapper.readTree(appResponse.getBody()).path("id").asText();
|
||||||
|
appSlugRef = appSlug;
|
||||||
|
|
||||||
// Upload a JAR version
|
// Upload a JAR version
|
||||||
byte[] jarContent = "fake-jar-for-deploy".getBytes();
|
byte[] jarContent = "fake-jar-for-deploy".getBytes();
|
||||||
@@ -85,7 +88,7 @@ class DeploymentControllerIT extends AbstractPostgresIT {
|
|||||||
headers.set("X-Cameleer-Protocol-Version", "1");
|
headers.set("X-Cameleer-Protocol-Version", "1");
|
||||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||||
ResponseEntity<String> versionResponse = restTemplate.exchange(
|
ResponseEntity<String> versionResponse = restTemplate.exchange(
|
||||||
"/api/v1/apps/" + appId + "/versions", HttpMethod.POST,
|
"/api/v1/environments/default/apps/" + appSlug + "/versions", HttpMethod.POST,
|
||||||
new HttpEntity<>(body, headers),
|
new HttpEntity<>(body, headers),
|
||||||
String.class);
|
String.class);
|
||||||
versionId = objectMapper.readTree(versionResponse.getBody()).path("id").asText();
|
versionId = objectMapper.readTree(versionResponse.getBody()).path("id").asText();
|
||||||
@@ -95,11 +98,11 @@ class DeploymentControllerIT extends AbstractPostgresIT {
|
|||||||
void deploy_asOperator_returns202() throws Exception {
|
void deploy_asOperator_returns202() throws Exception {
|
||||||
// Deploy creates a record; the async executor will fail (no Docker) but the record should exist
|
// Deploy creates a record; the async executor will fail (no Docker) but the record should exist
|
||||||
String json = String.format("""
|
String json = String.format("""
|
||||||
{"appVersionId": "%s", "environmentId": "%s"}
|
{"appVersionId": "%s"}
|
||||||
""", versionId, defaultEnvId);
|
""", versionId);
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/apps/" + appId + "/deployments", HttpMethod.POST,
|
"/api/v1/environments/default/apps/" + appSlugRef + "/deployments", HttpMethod.POST,
|
||||||
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
@@ -114,15 +117,15 @@ class DeploymentControllerIT extends AbstractPostgresIT {
|
|||||||
void listDeployments_asOperator_returnsDeployments() throws Exception {
|
void listDeployments_asOperator_returnsDeployments() throws Exception {
|
||||||
// Create a deployment first
|
// Create a deployment first
|
||||||
String json = String.format("""
|
String json = String.format("""
|
||||||
{"appVersionId": "%s", "environmentId": "%s"}
|
{"appVersionId": "%s"}
|
||||||
""", versionId, defaultEnvId);
|
""", versionId);
|
||||||
restTemplate.exchange(
|
restTemplate.exchange(
|
||||||
"/api/v1/apps/" + appId + "/deployments", HttpMethod.POST,
|
"/api/v1/environments/default/apps/" + appSlugRef + "/deployments", HttpMethod.POST,
|
||||||
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
new HttpEntity<>(json, securityHelper.authHeaders(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/apps/" + appId + "/deployments", HttpMethod.GET,
|
"/api/v1/environments/default/apps/" + appSlugRef + "/deployments", HttpMethod.GET,
|
||||||
new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
|
new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
@@ -135,7 +138,7 @@ class DeploymentControllerIT extends AbstractPostgresIT {
|
|||||||
@Test
|
@Test
|
||||||
void getDeployment_notFound_returns404() {
|
void getDeployment_notFound_returns404() {
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/apps/" + appId + "/deployments/00000000-0000-0000-0000-000000000000",
|
"/api/v1/environments/default/apps/" + appSlugRef + "/deployments/00000000-0000-0000-0000-000000000000",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
|
new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ class SearchControllerIT extends AbstractPostgresIT {
|
|||||||
private ResponseEntity<String> searchGet(String queryString) {
|
private ResponseEntity<String> searchGet(String queryString) {
|
||||||
HttpHeaders headers = securityHelper.authHeadersNoBody(jwt);
|
HttpHeaders headers = securityHelper.authHeadersNoBody(jwt);
|
||||||
return restTemplate.exchange(
|
return restTemplate.exchange(
|
||||||
"/api/v1/search/executions" + queryString,
|
"/api/v1/environments/default/executions" + queryString,
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
new HttpEntity<>(headers),
|
new HttpEntity<>(headers),
|
||||||
String.class);
|
String.class);
|
||||||
@@ -383,7 +383,7 @@ class SearchControllerIT extends AbstractPostgresIT {
|
|||||||
|
|
||||||
private ResponseEntity<String> searchPost(String jsonBody) {
|
private ResponseEntity<String> searchPost(String jsonBody) {
|
||||||
return restTemplate.exchange(
|
return restTemplate.exchange(
|
||||||
"/api/v1/search/executions",
|
"/api/v1/environments/default/executions/search",
|
||||||
HttpMethod.POST,
|
HttpMethod.POST,
|
||||||
new HttpEntity<>(jsonBody, securityHelper.authHeaders(viewerJwt)),
|
new HttpEntity<>(jsonBody, securityHelper.authHeaders(viewerJwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class JwtRefreshIT extends AbstractPostgresIT {
|
|||||||
authHeaders.set("X-Cameleer-Protocol-Version", "1");
|
authHeaders.set("X-Cameleer-Protocol-Version", "1");
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/search/executions",
|
"/api/v1/environments/default/executions",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
new HttpEntity<>(authHeaders),
|
new HttpEntity<>(authHeaders),
|
||||||
String.class);
|
String.class);
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class RegistrationSecurityIT extends AbstractPostgresIT {
|
|||||||
headers.set("X-Cameleer-Protocol-Version", "1");
|
headers.set("X-Cameleer-Protocol-Version", "1");
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
"/api/v1/search/executions",
|
"/api/v1/environments/default/executions",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
new HttpEntity<>(headers),
|
new HttpEntity<>(headers),
|
||||||
String.class);
|
String.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user