Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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("<!DOCTYPE html><html><head><title>" + AIRFLOW_TITLE + "</title></head>" //
+ "<body><h2>" + AIRFLOW_TITLE + "</h2></body></html>");

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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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("<html><body><a href=\"/airflow/home\">home</a></body></html>");

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\"")));
}
}
Loading