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
1 change: 1 addition & 0 deletions bridgeService-data/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<description>Test Library for the Bridge Service</description>
<properties>
<sonar.skip>true</sonar.skip>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<dependencies>
<dependency>
Expand Down
83 changes: 83 additions & 0 deletions bridgeService-test-injection/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright 2022 Adobe
All Rights Reserved.

NOTICE: Adobe permits you to use, modify, and distribute this file in
accordance with the terms of the Adobe license agreement accompanying
it.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" 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">
<modelVersion>4.0.0</modelVersion>

<groupId>com.adobe.campaign.tests.bridge</groupId>
<artifactId>bridgeService-test-injection</artifactId>
<name>${project.groupId}:${project.artifactId}</name>
<description>Injection model E2E test harness for the Bridge Service</description>
<properties>
<sonar.skip>true</sonar.skip>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Annotation processor: embeds Javadoc from this module's classes into the JAR so that
MCPToolDiscovery can read them at runtime via therapi-runtime-javadoc. -->
<dependency>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>0.15.0</version>
<scope>provided</scope>
</dependency>
<!-- Project test data — DepFactory.makeMiddleMan() returns MiddleMan from here -->
<dependency>
<groupId>com.adobe.campaign.tests.bridge.testdata</groupId>
<artifactId>bridgeService-data</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- BridgeService runtime — used by E2E tests to start the service in-process -->
<dependency>
<groupId>com.adobe.campaign.tests.bridge.service</groupId>
<artifactId>integroBridgeService</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.5.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-path</artifactId>
<version>5.5.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<parent>
<groupId>com.adobe.campaign.tests.bridge</groupId>
<artifactId>parent</artifactId>
<version>3.11.4-SNAPSHOT</version>
</parent>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2022 Adobe
* All Rights Reserved.
*
* NOTICE: Adobe permits you to use, modify, and distribute this file in
* accordance with the terms of the Adobe license agreement accompanying
* it.
*/
package com.adobe.campaign.tests.bridge.dependency.caller;

import com.adobe.campaign.tests.bridge.dependency.factory.DepFactory;
import com.adobe.campaign.tests.bridge.dependency.model.DepResult;

/**
* Simulates project code that calls a dependency library in a static initializer.
* When this class's package and DepResult's package are in STATIC_INTEGRITY_PACKAGES
* but DepFactory's package is not, both the IBS classloader and the parent classloader
* end up loading DepResult, which causes a LinkageError.
*/
public class DepCaller {

private final static DepResult instantiatedStaticConstant = DepFactory.makeDepResult("initial");

/**
* Returns a fixed string, serving as the method to invoke via the /call endpoint in tests.
*
* @return a confirmation string
*/
public static String doSomething() {
return instantiatedStaticConstant.getValue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2022 Adobe
* All Rights Reserved.
*
* NOTICE: Adobe permits you to use, modify, and distribute this file in
* accordance with the terms of the Adobe license agreement accompanying
* it.
*/
package com.adobe.campaign.tests.bridge.dependency.factory;

import com.adobe.campaign.tests.bridge.dependency.model.DepResult;
import com.adobe.campaign.tests.bridge.testdata.issue34.pckg1.MiddleMan;

/**
* Simulated dependency library factory.
* Lives in a separate package from DepResult to enable the split-package classloader conflict scenario:
* when DepResult's package is in STATIC_INTEGRITY_PACKAGES but this factory's package is not,
* parent classloader loads DepResult a second time (as this method's return type), causing a LinkageError.
*/
public class DepFactory {

/**
* Creates a DepResult instance.
*
* @param in_value the string value to embed in the result
* @return a new DepResult
*/
public static DepResult makeDepResult(String in_value) {
return new DepResult(in_value);
}

/**
* Creates a MiddleMan instance from the project (bridgeService-data) test data.
* Used to demonstrate that a dependency library can return project types when
* both packages are included in STATIC_INTEGRITY_PACKAGES.
*
* @return a new MiddleMan instance
*/
public static MiddleMan makeMiddleMan() {
return new MiddleMan();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2022 Adobe
* All Rights Reserved.
*
* NOTICE: Adobe permits you to use, modify, and distribute this file in
* accordance with the terms of the Adobe license agreement accompanying
* it.
*/
package com.adobe.campaign.tests.bridge.dependency.model;

/**
* A result type produced by the simulated dependency library.
* Used by the injection model tests to trigger and verify classloader isolation behaviour.
*/
public class DepResult {

private String value;

public DepResult(String in_value) {
this.value = in_value;
}

public String getValue() {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2022 Adobe
* All Rights Reserved.
*
* NOTICE: Adobe permits you to use, modify, and distribute this file in
* accordance with the terms of the Adobe license agreement accompanying
* it.
*/
package com.adobe.campaign.tests.bridge.dependency;

import com.adobe.campaign.tests.bridge.service.CallContent;
import com.adobe.campaign.tests.bridge.service.ConfigValueHandlerIBS;
import com.adobe.campaign.tests.bridge.service.IntegroAPI;
import com.adobe.campaign.tests.bridge.service.JavaCalls;
import com.adobe.campaign.tests.bridge.service.exceptions.IBSConfigurationException;
import com.adobe.campaign.tests.bridge.dependency.caller.DepCaller;
import com.adobe.campaign.tests.bridge.dependency.factory.DepFactory;
import io.javalin.Javalin;
import org.hamcrest.Matchers;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import static io.restassured.RestAssured.given;

/**
* E2E tests for the injection model (IBS.CLASSLOADER.STATIC.INTEGRITY.PACKAGES).
* Demonstrates that dependency library packages can and must be listed alongside
* project packages to avoid LinkageError when the library's factory returns a type
* that is also loaded by the IBS classloader.
*/
public class InjectionModelE2ETests {

private static final String END_POINT_URL = "http://localhost:8080/";

private static final String CALLER_PACKAGE =
"com.adobe.campaign.tests.bridge.dependency.caller.";
private static final String MODEL_PACKAGE =
"com.adobe.campaign.tests.bridge.dependency.model.";
private static final String FACTORY_PACKAGE =
"com.adobe.campaign.tests.bridge.dependency.factory.";

private Javalin app;

@BeforeGroups(groups = "E2E")
public void startUpService() {
app = IntegroAPI.startServices(8080);
}

@BeforeMethod
public void cleanCache() {
ConfigValueHandlerIBS.resetAllValues();
}

@AfterGroups(groups = "E2E", alwaysRun = true)
public void tearDown() {
ConfigValueHandlerIBS.resetAllValues();
app.stop();
}

/**
* Negative test: when DepResult's package IS in STATIC_INTEGRITY_PACKAGES but
* DepFactory's package is NOT, the IBS classloader and the parent classloader both
* load DepResult independently, producing a LinkageError.
*/
@Test(groups = "E2E")
public void testInjectionConflict_missingFactoryPackage() {
ConfigValueHandlerIBS.INTEGRITY_PACKAGE_INJECTION_MODE.activate("manual");
ConfigValueHandlerIBS.STATIC_INTEGRITY_PACKAGES.activate(
MODEL_PACKAGE + "," + CALLER_PACKAGE);

JavaCalls l_myJavaCalls = new JavaCalls();
CallContent l_cc = new CallContent();
l_cc.setClassName(DepCaller.class.getTypeName());
l_cc.setMethodName("doSomething");
l_myJavaCalls.getCallContent().put("call1", l_cc);

given().body(l_myJavaCalls).post(END_POINT_URL + "call").then()
.assertThat().statusCode(500)
.body("title", Matchers.equalTo(
"The provided class and method for setting environment variables is not valid."))
.body("code", Matchers.equalTo(500))
.body("detail", Matchers.startsWith("Linkage Error detected"))
.body("bridgeServiceException",
Matchers.equalTo(IBSConfigurationException.class.getTypeName()))
.body("originalException",
Matchers.equalTo(LinkageError.class.getTypeName()));
}

/**
* Positive test: adding DepFactory's package to STATIC_INTEGRITY_PACKAGES ensures all
* three packages are loaded by the same IBS classloader, eliminating the type mismatch.
*/
@Test(groups = "E2E")
public void testInjectionConflict_allPackagesPresent() {
ConfigValueHandlerIBS.INTEGRITY_PACKAGE_INJECTION_MODE.activate("manual");
ConfigValueHandlerIBS.STATIC_INTEGRITY_PACKAGES.activate(
MODEL_PACKAGE + "," + CALLER_PACKAGE + "," + FACTORY_PACKAGE);

JavaCalls l_myJavaCalls = new JavaCalls();
CallContent l_cc = new CallContent();
l_cc.setClassName(DepCaller.class.getTypeName());
l_cc.setMethodName("doSomething");
l_myJavaCalls.getCallContent().put("call1", l_cc);

given().body(l_myJavaCalls).post(END_POINT_URL + "call").then()
.assertThat().statusCode(200)
.body("returnValues.call1", Matchers.equalTo("initial"));
}

/**
* Cross-module type test: DepFactory.makeMiddleMan() returns a MiddleMan from bridgeService-data.
* When the factory package is in STATIC_INTEGRITY_PACKAGES, the call succeeds and the
* MiddleMan object is serialised correctly by the BridgeService response layer.
*/
@Test(groups = "E2E")
public void testDepFactoryReturnsBridgeDataType() {
ConfigValueHandlerIBS.INTEGRITY_PACKAGE_INJECTION_MODE.activate("manual");
ConfigValueHandlerIBS.STATIC_INTEGRITY_PACKAGES.activate(FACTORY_PACKAGE);

JavaCalls l_myJavaCalls = new JavaCalls();
CallContent l_cc = new CallContent();
l_cc.setClassName(DepFactory.class.getTypeName());
l_cc.setMethodName("makeMiddleMan");
l_myJavaCalls.getCallContent().put("call1", l_cc);

given().body(l_myJavaCalls).post(END_POINT_URL + "call").then()
.assertThat().statusCode(200);
}
}
Loading
Loading