Skip to content
Open
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
120 changes: 118 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ dependencies {
testImplementation("com.google.guava:guava:33.0.0-jre")
testImplementation("com.squareup.okhttp3:mockwebserver:4.12.0")

// Cucumber for BDD
// Cucumber for BDD
testImplementation("io.cucumber:cucumber-java:7.27.2")
testImplementation("io.cucumber:cucumber-junit-platform-engine:7.27.2")

Expand Down Expand Up @@ -166,7 +166,45 @@ tasks.register<Test>("advancedTokenTests") {
shouldRunAfter(tasks.test)
}

// ✅ Run all cucumber tests (including integration)
tasks.register<Test>("shardTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@shard-routing")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

tasks.register<Test>("multiAggregatorTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@multi-aggregator and not @ignore")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

tasks.register<Test>("perfTests") {
useJUnitPlatform()
maxHeapSize = "2048m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@performance")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

tasks.register<Test>("allCucumberTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
Expand All @@ -180,6 +218,84 @@ tasks.register<Test>("allCucumberTests") {
shouldRunAfter(tasks.test)
}

tasks.register<Test>("lifecycleTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@token-lifecycle")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

tasks.register<Test>("predicateMatrixTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@predicate-matrix")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

tasks.register<Test>("authorizationTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@authorization")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

tasks.register<Test>("splitBoundaryTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@split-boundaries")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

tasks.register<Test>("multiLevelSplitTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@multi-level-split")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

tasks.register<Test>("edgeCaseTests") {
useJUnitPlatform()
maxHeapSize = "1024m"
systemProperty("cucumber.junit-platform.naming-strategy", "long")
systemProperties = System.getProperties().toMap() as Map<String, Any>
systemProperty("cucumber.filter.tags", "@edge-cases")

filter {
includeTestsMatching("*CucumberTestRunner*")
}
shouldRunAfter(tasks.test)
}

// Create separate JARs for each platform
tasks.register<Jar>("androidJar") {
archiveClassifier.set("android")
Expand Down
38 changes: 20 additions & 18 deletions src/test/java/org/unicitylabs/sdk/e2e/CucumberTestRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
import org.junit.platform.suite.api.*;

/**
* Updated Cucumber test runner configuration for E2E tests.
* This class configures the test execution environment and feature discovery
* with the new shared step definitions approach.
* Cucumber test runner configuration for E2E tests.
* Configures test execution environment and feature discovery.
*
* Safety: tasks.test excludes *CucumberTestRunner* — cucumber tests
* only run via dedicated gradle tasks (tokenTests, aggregatorTests, etc.)
*/
//@Suite
//@IncludeEngines("cucumber")
//@SelectPackages("org.unicitylabs.sdk.features")
//@ConfigurationParameter(key = Constants.GLUE_PROPERTY_NAME, value = "org.unicitylabs.sdk.e2e.steps,org.unicitylabs.sdk.e2e.steps.shared,org.unicitylabs.sdk.e2e.config")
//@ConfigurationParameter(key = Constants.PLUGIN_PROPERTY_NAME, value = "pretty,html:build/cucumber-reports/cucumber.html,json:build/cucumber-reports/cucumber.json")
//@ConfigurationParameter(key = Constants.EXECUTION_DRY_RUN_PROPERTY_NAME, value = "false")
//@ConfigurationParameter(key = Constants.PLUGIN_PUBLISH_QUIET_PROPERTY_NAME, value = "true")
//public class CucumberTestRunner {
// static {
// // Only set default tags if no tags are specified
// if (System.getProperty("cucumber.filter.tags") == null) {
// System.setProperty("cucumber.filter.tags", "not @ignore");
// }
// }
//}
@Suite
@IncludeEngines("cucumber")
@SelectPackages("org.unicitylabs.sdk.features")
@ConfigurationParameter(key = Constants.GLUE_PROPERTY_NAME, value = "org.unicitylabs.sdk.e2e.steps,org.unicitylabs.sdk.e2e.steps.shared,org.unicitylabs.sdk.e2e.config")
@ConfigurationParameter(key = Constants.PLUGIN_PROPERTY_NAME, value = "pretty,html:build/cucumber-reports/cucumber.html,json:build/cucumber-reports/cucumber.json")
@ConfigurationParameter(key = Constants.EXECUTION_DRY_RUN_PROPERTY_NAME, value = "false")
@ConfigurationParameter(key = Constants.PLUGIN_PUBLISH_QUIET_PROPERTY_NAME, value = "true")
public class CucumberTestRunner {
static {
// Only set default tags if no tags are specified
if (System.getProperty("cucumber.filter.tags") == null) {
System.setProperty("cucumber.filter.tags", "not @ignore");
}
}
}
104 changes: 104 additions & 0 deletions src/test/java/org/unicitylabs/sdk/e2e/config/AggregatorConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package org.unicitylabs.sdk.e2e.config;

import java.util.HashMap;
import java.util.Map;

/**
* Centralized aggregator URL and shard topology configuration.
*
* Standalone: sdk.aggregator.url or AGGREGATOR_URL (default http://localhost:3000).
* Sharded: sdk.shard.id.length + sdk.shard.<id>.url for each shard.
* Explicit shard-to-URL mapping — no ordering concerns.
*/
public final class AggregatorConfig {

private AggregatorConfig() {
}

/**
* Returns the single aggregator URL.
* Resolution order: system property sdk.aggregator.url, env AGGREGATOR_URL,
* default http://localhost:3000.
*/
public static String getSingleUrl() {
String url = System.getProperty("sdk.aggregator.url");
if (url != null && !url.isEmpty()) {
return url;
}
url = System.getenv("AGGREGATOR_URL");
if (url != null && !url.isEmpty()) {
return url;
}
return "http://192.168.43.106:3000";
}

/**
* Returns the shard ID length.
* Resolution order: system property sdk.shard.id.length, env SHARD_ID_LENGTH,
* default 1.
*/
public static int getShardIdLength() {
String val = System.getProperty("sdk.shard.id.length");
if (val != null && !val.isEmpty()) {
return Integer.parseInt(val);
}
val = System.getenv("SHARD_ID_LENGTH");
if (val != null && !val.isEmpty()) {
return Integer.parseInt(val);
}
return 1;
}

/**
* Returns an explicit {shardId -> url} mapping for all expected shards.
*
* Shard IDs use a leading 1-bit prefix:
* shardIdLength=1 -> shardIds: 2, 3 (binary: 10, 11)
* shardIdLength=2 -> shardIds: 4, 5, 6, 7 (binary: 100, 101, 110, 111)
*
* Per-shard URL resolution: system property sdk.shard.<id>.url, then env SHARD_<id>_URL.
*
* @throws IllegalStateException if any expected shard is missing a URL
*/
public static Map<Integer, String> getShardUrlMap() {
int shardIdLength = getShardIdLength();
int baseId = 1 << shardIdLength;
int shardCount = 1 << shardIdLength;

Map<Integer, String> map = new HashMap<>();
java.util.List<Integer> missing = new java.util.ArrayList<>();

for (int i = 0; i < shardCount; i++) {
int shardId = baseId + i;
String url = resolveShardUrl(shardId);
if (url == null || url.isEmpty()) {
missing.add(shardId);
} else {
map.put(shardId, url);
}
}

if (!missing.isEmpty()) {
throw new IllegalStateException(
"Missing shard URL configuration for shard IDs: " + missing
+ ". Configure via -Dsdk.shard.<id>.url=<url> or env SHARD_<id>_URL"
);
}

return map;
}

private static String resolveShardUrl(int shardId) {
String propKey = "sdk.shard." + shardId + ".url";
String url = System.getProperty(propKey);
if (url != null && !url.isEmpty()) {
return url;
}
String envKey = "SHARD_" + shardId + "_URL";
url = System.getenv(envKey);
if (url != null && !url.isEmpty()) {
return url;
}
return null;
}
}
Loading
Loading