diff --git a/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/AirflowPerspectiveIT.java b/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/AirflowPerspectiveIT.java
index 64e3d94..1a77184 100644
--- a/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/AirflowPerspectiveIT.java
+++ b/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/AirflowPerspectiveIT.java
@@ -12,19 +12,34 @@
import com.codbex.phoebe.cfg.AppConfig;
import org.eclipse.dirigible.tests.framework.browser.HtmlElementType;
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
class AirflowPerspectiveIT extends PhoebeIntegrationTest {
+ private static final String AIRFLOW_TITLE = "Apache Airflow";
+
+ // A local stub stands in for the Airflow instance so the test does not depend on an external
+ // host. The URL must be configured before the Spring context (and the proxy beans) start, hence
+ // the static initializer.
+ private static final LocalHttpStub AIRFLOW_STUB =
+ LocalHttpStub.startServingHtml("
" + AIRFLOW_TITLE + "" //
+ + "" + AIRFLOW_TITLE + "
");
+
static {
- AppConfig.AIRFLOW_URL.setValue("http://httpbin.org");
+ AppConfig.AIRFLOW_URL.setValue(AIRFLOW_STUB.baseUrl());
+ }
+
+ @AfterAll
+ static void stopStub() {
+ AIRFLOW_STUB.stop();
}
@Test
void testPerspective() {
- // expected to open https://httpbin.org
+ // the perspective iframes the proxied Airflow UI served by the local stub
ide.openPath("/services/web/perspective-airflow/index.html");
- browser.assertElementExistsByTypeAndContainsText(HtmlElementType.HEADER2, "httpbin.org");
+ browser.assertElementExistsByTypeAndContainsText(HtmlElementType.HEADER2, AIRFLOW_TITLE);
}
}
diff --git a/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/LocalHttpStub.java b/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/LocalHttpStub.java
new file mode 100644
index 0000000..baec6cd
--- /dev/null
+++ b/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/LocalHttpStub.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2022 codbex or an codbex affiliate company and contributors
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ *
+ * SPDX-FileCopyrightText: 2022 codbex or an codbex affiliate company and contributors
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package com.codbex.phoebe.integration.tests;
+
+import com.sun.net.httpserver.HttpServer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * A minimal, self-contained HTTP server backed by the JDK (no external dependency) used as the
+ * upstream "Airflow" target in proxy integration tests. It serves a fixed HTML body for every
+ * request on an OS-assigned ephemeral port, which makes the proxy tests hermetic instead of relying
+ * on flaky external hosts (e.g. httpbin.org).
+ */
+final class LocalHttpStub {
+
+ private final HttpServer server;
+ private final String baseUrl;
+
+ private LocalHttpStub(HttpServer server) {
+ this.server = server;
+ this.baseUrl = "http://localhost:" + server.getAddress()
+ .getPort();
+ }
+
+ /**
+ * Starts a stub server that replies to every request with the given HTML body.
+ *
+ * @param htmlBody the response body served as {@code text/html}
+ * @return the started stub; the caller is responsible for {@link #stop()}
+ */
+ static LocalHttpStub startServingHtml(String htmlBody) {
+ try {
+ HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
+ byte[] body = htmlBody.getBytes(StandardCharsets.UTF_8);
+ server.createContext("/", exchange -> {
+ exchange.getResponseHeaders()
+ .set("Content-Type", "text/html; charset=utf-8");
+ exchange.sendResponseHeaders(200, body.length);
+ try (OutputStream responseBody = exchange.getResponseBody()) {
+ responseBody.write(body);
+ }
+ });
+ server.start();
+ return new LocalHttpStub(server);
+ } catch (IOException ex) {
+ throw new IllegalStateException("Failed to start local HTTP stub server", ex);
+ }
+ }
+
+ /**
+ * @return the base URL (scheme, host and port) the stub is listening on
+ */
+ String baseUrl() {
+ return baseUrl;
+ }
+
+ void stop() {
+ server.stop(0);
+ }
+}
diff --git a/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/ProxyIT.java b/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/ProxyIT.java
index de27e13..23f8eda 100644
--- a/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/ProxyIT.java
+++ b/integration-tests/src/test/java/com/codbex/phoebe/integration/tests/ProxyIT.java
@@ -12,30 +12,42 @@
import com.codbex.phoebe.cfg.AppConfig;
import org.eclipse.dirigible.tests.framework.restassured.RestAssuredExecutor;
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static io.restassured.RestAssured.given;
-import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.containsString;
class ProxyIT extends PhoebeIntegrationTest {
private static final String AIRFLOW_PROXY_PATH = "/services/airflow";
+ // The stub returns a root-relative link; the proxy is expected to rewrite it so it stays within
+ // the "/services/airflow" prefix when the UI is served behind the proxy.
+ private static final LocalHttpStub AIRFLOW_STUB =
+ LocalHttpStub.startServingHtml("home");
+
static {
- AppConfig.AIRFLOW_URL.setValue("https://api.ipify.org");
+ AppConfig.AIRFLOW_URL.setValue(AIRFLOW_STUB.baseUrl());
}
@Autowired
private RestAssuredExecutor restAssuredExecutor;
+ @AfterAll
+ static void stopStub() {
+ AIRFLOW_STUB.stop();
+ }
+
@Test
void textProxyPath() {
- // expected to open https://api.ipify.org?format=json
+ // the proxy forwards to the local stub and rewrites the root-relative link in the response
+ // body from "/airflow/home" to "/services/airflow/airflow/home"
restAssuredExecutor.execute(() -> given().when()
- .get(AIRFLOW_PROXY_PATH + "?format=json")
+ .get(AIRFLOW_PROXY_PATH + "/home")
.then()
.statusCode(200)
- .body("ip", notNullValue()));
+ .body(containsString("href=\"" + AIRFLOW_PROXY_PATH + "/airflow/home\"")));
}
}