Skip to content
Open
20 changes: 13 additions & 7 deletions .github/workflows/unomi-ci-build-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
sudo apt-get install -y graphviz
dot -V
- name: Build and Unit tests
run: mvn -U -ntp -e clean install
run: ./build.sh --ci

integration-tests:
name: Execute integration tests
Expand All @@ -54,16 +54,22 @@ jobs:
distribution: 'temurin'
java-version: '17'
cache: 'maven'
- name: Install GraphViz
run: |
sudo apt-get update
sudo apt-get install -y graphviz
dot -V
- name: Integration tests
env:
MAVEN_EXTRA_OPTS: >-
-Dopensearch.port=${{ matrix.port }}
-Delasticsearch.port=${{ matrix.port }}
run: |
FLAGS="-Pintegration-tests"
if [ "${{ matrix.search-engine }}" = "opensearch" ]; then
# Trigger OpenSearch profile activation via property; do not pass any -P profile toggles
FLAGS="$FLAGS -Duse.opensearch=true"
./build.sh --ci --integration-tests --use-opensearch
else
./build.sh --ci --integration-tests
fi
mvn -ntp clean install $FLAGS \
-Dopensearch.port=${{ matrix.port }} \
-Delasticsearch.port=${{ matrix.port }}
- name: Archive code coverage logs
uses: actions/upload-artifact@v4
if: false # UNOMI-746 Reactivate if necessary
Expand Down
65 changes: 48 additions & 17 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@
################################################################################

set -e # Exit on error
trap 'handle_error $? $LINENO $BASH_LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]:-})' ERR
# Keep trap arguments small: passing full $BASH_COMMAND can exceed ARG_MAX after a failed mvn invocation.
trap 'handle_error $? $LINENO' ERR

# Error handling function
handle_error() {
local exit_code=$1
local line_no=$2
local bash_lineno=$3
local last_command=$4
local func_trace=$5

cat << "EOF"
_____ ____ ____ ___ ____
Expand All @@ -38,12 +36,8 @@ handle_error() {

EOF
echo "Error occurred in:"
echo " Command: $last_command"
echo " Line: $line_no"
echo " Exit code: $exit_code"
if [ ! -z "$func_trace" ]; then
echo " Function trace: $func_trace"
fi
exit $exit_code
Comment on lines 22 to 41
}

Expand Down Expand Up @@ -222,13 +216,23 @@ print_progress() {
fi
}

# Non-interactive when run from CI or when explicitly requested (e.g. GitHub Actions).
is_non_interactive() {
[ -n "${CI:-}" ] || [ -n "${GITHUB_ACTIONS:-}" ] || [ "${BUILD_NON_INTERACTIVE:-}" = "true" ]
}

# Function to prompt for continuation
prompt_continue() {
local prompt_text="$1"
if [ -z "$prompt_text" ]; then
prompt_text="Continue?"
fi


if is_non_interactive; then
print_status "info" "Non-interactive mode: continuing ($prompt_text)"
return 0
fi

read -p "$prompt_text (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
Expand Down Expand Up @@ -296,6 +300,7 @@ EOF
echo -e " ${CYAN}--it-debug-port PORT${NC} Set integration test debug port"
echo -e " ${CYAN}--it-debug-suspend${NC} Suspend integration test until debugger connects"
echo -e " ${CYAN}--skip-migration-tests${NC} Skip migration-related tests"
echo -e " ${CYAN}--ci${NC} CI mode: no Karaf, no Maven cache, Maven -B -ntp, non-interactive"
else
cat << "EOF"
_ _ _____ _ ____
Expand Down Expand Up @@ -329,6 +334,7 @@ EOF
echo " --it-debug-port PORT Set integration test debug port"
echo " --it-debug-suspend Suspend integration test until debugger connects"
echo " --skip-migration-tests Skip migration-related tests"
echo " --ci CI mode: no Karaf, no Maven cache, Maven -B -ntp, non-interactive"
fi

echo
Expand Down Expand Up @@ -459,6 +465,11 @@ while [ "$1" != "" ]; do
--skip-migration-tests)
SKIP_MIGRATION_TESTS=true
;;
--ci)
NO_KARAF=true
USE_MAVEN_CACHE=false
BUILD_NON_INTERACTIVE=true
;;
*)
echo "Unknown option: $1"
usage
Expand Down Expand Up @@ -770,6 +781,11 @@ check_requirements() {
MVN_CMD="mvn"
MVN_OPTS=""

# CI / non-interactive: no download progress UI, batch mode (matches former workflow mvn -ntp)
if is_non_interactive; then
MVN_OPTS="$MVN_OPTS -B -ntp"
fi

# Add Maven debug option
if [ "$MAVEN_DEBUG" = true ]; then
MVN_OPTS="$MVN_OPTS -X"
Expand All @@ -784,10 +800,14 @@ if [ "$MAVEN_OFFLINE" = true ]; then
# Warn if purge cache is enabled with offline mode
if [ "$PURGE_MAVEN_CACHE" = true ]; then
echo "Warning: Purging Maven cache while in offline mode may cause build failures"
read -p "Continue anyway? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
if is_non_interactive; then
print_status "warning" "Non-interactive mode: continuing despite purge + offline"
else
read -p "Continue anyway? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
fi
fi
Expand All @@ -797,13 +817,22 @@ if [ "$USE_MAVEN_CACHE" = false ]; then
MVN_OPTS="$MVN_OPTS -Dmaven.build.cache.enabled=false"
fi

# Extra Maven options (e.g. CI matrix ports: -Delasticsearch.port=9400)
if [ -n "${MAVEN_EXTRA_OPTS:-}" ]; then
MVN_OPTS="$MVN_OPTS $MAVEN_EXTRA_OPTS"
fi
Comment on lines +820 to +823

# Verify Maven settings
if [ ! -f ~/.m2/settings.xml ]; then
echo "Warning: Maven settings.xml not found at ~/.m2/settings.xml"
read -p "Continue anyway? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
if is_non_interactive; then
print_status "info" "Non-interactive mode: continuing without ~/.m2/settings.xml"
else
read -p "Continue anyway? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
fi

Expand Down Expand Up @@ -900,6 +929,7 @@ if [ "$HAS_COLORS" -eq 1 ]; then
else
echo "Running: $MVN_CMD clean $MVN_OPTS"
fi
# shellcheck disable=SC2086
$MVN_CMD clean $MVN_OPTS || {
print_status "error" "Maven clean failed"
exit 1
Expand All @@ -911,6 +941,7 @@ if [ "$HAS_COLORS" -eq 1 ]; then
else
echo "Running: $MVN_CMD install $MVN_OPTS"
fi
# shellcheck disable=SC2086
$MVN_CMD install $MVN_OPTS || {
print_status "error" "Maven install failed"
exit 1
Expand Down
8 changes: 8 additions & 0 deletions itests/src/test/java/org/apache/unomi/itests/BaseIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,14 @@ protected <T> T keepTrying(String failMessage, Supplier<T> call, Predicate<T> pr
return value;
}

protected void waitForProfileProperty(String profileId, String propertyName, Object expected)
throws InterruptedException {
keepTrying("Profile " + profileId + " property " + propertyName + " not updated",
() -> profileService.load(profileId),
profile -> profile != null && java.util.Objects.equals(expected, profile.getProperty(propertyName)),
DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
}

protected <T> void waitForNullValue(String failMessage, Supplier<T> call, int timeout, int retries) throws InterruptedException {
int count = 0;
while (call.get() != null) {
Expand Down
28 changes: 20 additions & 8 deletions itests/src/test/java/org/apache/unomi/itests/PatchIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class PatchIT extends BaseIT {
private Logger LOGGER = LoggerFactory.getLogger(PatchIT.class);

@Test
public void testPatch() throws IOException {
public void testPatch() throws IOException, InterruptedException {
PropertyType company = profileService.getPropertyType("company");

try {
Expand All @@ -49,15 +49,18 @@ public void testPatch() throws IOException {

profileService.refresh();

newCompany = profileService.getPropertyType("company");
newCompany = keepTrying("Failed waiting for patched property type",
() -> profileService.getPropertyType("company"),
pt -> pt != null && "foo".equals(pt.getDefaultValue()),
DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
Assert.assertEquals("foo", newCompany.getDefaultValue());
} finally {
profileService.setPropertyType(company);
}
}

@Test
public void testOverride() throws IOException {
public void testOverride() throws IOException, InterruptedException {
PropertyType gender = profileService.getPropertyType("gender");

try {
Expand All @@ -68,7 +71,10 @@ public void testOverride() throws IOException {

profileService.refresh();

newGender = profileService.getPropertyType("gender");
newGender = keepTrying("Failed waiting for patched property type",
() -> profileService.getPropertyType("gender"),
pt -> pt != null && "foo".equals(pt.getDefaultValue()),
DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
Assert.assertEquals("foo", newGender.getDefaultValue());
} finally {
profileService.setPropertyType(gender);
Expand All @@ -86,8 +92,8 @@ public void testRemove() throws IOException, InterruptedException {

profileService.refresh();

PropertyType newIncome = profileService.getPropertyType("income");
Assert.assertNull(newIncome);
waitForNullValue("Failed waiting for property type removal",
() -> profileService.getPropertyType("income"), DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
} finally {
profileService.setPropertyType(income);
}
Expand All @@ -105,7 +111,10 @@ public void testPatchOnConditionType() throws IOException, InterruptedException

definitionsService.refresh();

ConditionType newFormCondition = definitionsService.getConditionType("formEventCondition");
ConditionType newFormCondition = keepTrying("Failed waiting for patched condition type",
() -> definitionsService.getConditionType("formEventCondition"),
ct -> ct != null && !ct.getMetadata().getSystemTags().contains("profileTags"),
DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
Assert.assertFalse(newFormCondition.getMetadata().getSystemTags().contains("profileTags"));
} finally {
definitionsService.setConditionType(formCondition);
Expand All @@ -124,7 +133,10 @@ public void testPatchOnActionType() throws IOException, InterruptedException {

definitionsService.refresh();

ActionType newMailAction = definitionsService.getActionType("sendMailAction");
ActionType newMailAction = keepTrying("Failed waiting for patched action type",
() -> definitionsService.getActionType("sendMailAction"),
at -> at != null && !at.getMetadata().getSystemTags().contains("availableToEndUser"),
DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
Assert.assertFalse(newMailAction.getMetadata().getSystemTags().contains("availableToEndUser"));
} finally {
definitionsService.setActionType(mailAction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,12 @@ public void testUpdateProperties_CurrentProfile() {
LOGGER.info("Changes of the event : {}", changes);

Assert.assertTrue(changes > 0);
// Current profile on the event is updated in memory; do not poll persistence here.
Assert.assertEquals("UPDATED FIRST NAME CURRENT PROFILE", profile.getProperty("firstName"));
}

@Test
public void testUpdateProperties_NotCurrentProfile() {
public void testUpdateProperties_NotCurrentProfile() throws InterruptedException {
Profile profile = profileService.load(PROFILE_TARGET_TEST_ID);
Profile profileToUpdate = profileService.load(PROFILE_TEST_ID);
Assert.assertNull(profileToUpdate.getProperty("firstName"));
Expand All @@ -117,8 +118,7 @@ public void testUpdateProperties_NotCurrentProfile() {
updateProperties.setProperty(UpdatePropertiesAction.TARGET_TYPE_KEY, "profile");
eventService.send(updateProperties);

profileToUpdate = profileService.load(PROFILE_TEST_ID);
Assert.assertEquals("UPDATED FIRST NAME", profileToUpdate.getProperty("firstName"));
waitForProfileProperty(PROFILE_TEST_ID, "firstName", "UPDATED FIRST NAME");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,28 @@ public void testCRUD() throws Exception {

refreshPersistence(UserList.class);

Thread.sleep(6000);

try (CloseableHttpResponse response = post("graphql/list/find-lists.json")) {
final ResponseContext context = ResponseContext.parse(response.getEntity());

Assert.assertEquals(1, ((Integer) context.getValue("data.cdp.findLists.totalCount")).intValue());
Assert.assertEquals("testListId", context.getValue("data.cdp.findLists.edges[0].node.id"));
Assert.assertEquals(profile.getItemId(), context.getValue("data.cdp.findLists.edges[0].node.active.edges[0].node.cdp_profileIDs[0].id"));
}
final ResponseContext findListsContext = keepTrying("Failed waiting for profile in list query",
() -> {
try (CloseableHttpResponse response = post("graphql/list/find-lists.json")) {
return ResponseContext.parse(response.getEntity());
} catch (Exception e) {
return null;
}
},
context -> {
if (context == null) {
return false;
}
Integer totalCount = (Integer) context.getValue("data.cdp.findLists.totalCount");
if (totalCount == null || totalCount != 1) {
return false;
}
Object profileId = context.getValue("data.cdp.findLists.edges[0].node.active.edges[0].node.cdp_profileIDs[0].id");
return profile.getItemId().equals(profileId);
},
DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
Comment on lines +80 to +99

Assert.assertEquals("testListId", findListsContext.getValue("data.cdp.findLists.edges[0].node.id"));

try (CloseableHttpResponse response = post("graphql/list/delete-list.json")) {
final ResponseContext context = ResponseContext.parse(response.getEntity());
Expand Down
9 changes: 7 additions & 2 deletions setenv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@
# limitations under the License.
#
################################################################################
export UNOMI_VERSION=`mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -Ev '(^\[|Download\w+:)'`
echo Detected project version=$UNOMI_VERSION
# Quiet evaluate: avoid capturing Maven download lines into the environment (breaks CI with ARG_MAX).
export UNOMI_VERSION="$(mvn -B -q -DforceStdout help:evaluate -Dexpression=project.version -DinteractiveMode=false 2>/dev/null)"
if [ -z "$UNOMI_VERSION" ]; then
echo "Failed to detect project version from Maven" >&2
exit 1
fi
Comment on lines +21 to +25
echo "Detected project version=$UNOMI_VERSION"
export KARAF_VERSION=4.4.8
# Uncomment the following line if you need Apache Unomi to start automatically at the first start
# export KARAF_OPTS="-Dunomi.autoStart=true"
Loading