Skip to content

Commit 96fd95a

Browse files
b3y0urs3lfclaude
andcommitted
Add BDD test suite covering V2 SDK functionality
Ports the TypeScript SDK's BDD coverage to Java, mirroring scenarios across aggregator communication, token minting/transfer/verification, splits, payments, sharding, CBOR envelope strictness, and inclusion proofs. Covers the V2 PR chain (#53, #54, #59, #61, #62, #63, #65) end-to-end against both the hermetic in-process aggregator and the real BFT-shard topology via subscription proxy. - 49 Cucumber feature files (~149 scenario declarations) - 33 step-definition classes wired through PicoContainer - Test-only support clients: StrictTestAggregatorClient, ForgivingTestAggregatorClient, ShardAwareAggregatorClient, SimulatedShardPool, NametagRegistry, TokenTree(Builder) - Tag-based runtime gating: @hermetic-only, @bft-shard-only, @multi-shard-only, @pending-src-cleanup, @stateful, @fresh-aggregator, @slow - Env wiring: AGGREGATOR_URL, AGGREGATOR_API_KEY, TRUST_BASE_PATH - JUnit Platform Suite runner with package selection Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 64ee533 commit 96fd95a

93 files changed

Lines changed: 8092 additions & 201 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build.gradle.kts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ dependencies {
6868
// ✅ Cucumber for BDD
6969
testImplementation("io.cucumber:cucumber-java:7.27.2")
7070
testImplementation("io.cucumber:cucumber-junit-platform-engine:7.27.2")
71+
testImplementation("io.cucumber:cucumber-picocontainer:7.27.2")
7172

7273
// JUnit 5 Suite annotations
7374
testImplementation("org.junit.platform:junit-platform-suite:1.13.4")
@@ -180,6 +181,112 @@ tasks.register<Test>("allCucumberTests") {
180181
shouldRunAfter(tasks.test)
181182
}
182183

184+
// ─── BDD tasks (Phase 0 of BDD migration plan) ───────────────────────────────
185+
// Default BDD run: defers to CucumberTestRunner's static-init default tag
186+
// filter, which is "not @nametag and not @slow and not @wip and not @ignore"
187+
// but yields to an explicit -Dcucumber.filter.tags=<expr> override.
188+
tasks.register<Test>("bddTest") {
189+
useJUnitPlatform()
190+
maxHeapSize = "1024m"
191+
systemProperty("cucumber.junit-platform.naming-strategy", "long")
192+
systemProperties = System.getProperties().toMap() as Map<String, Any>
193+
194+
// Make BDD env vars part of the task's up-to-date cache key so that
195+
// switching AGGREGATOR_URL / TRUST_BASE_PATH / tag filter actually forces a
196+
// re-run. Without this, Gradle sees unchanged source+classpath and serves
197+
// a cached pass in ~3 seconds.
198+
inputs.property("AGGREGATOR_URL", System.getenv("AGGREGATOR_URL") ?: "")
199+
inputs.property("AGGREGATOR_API_KEY", System.getenv("AGGREGATOR_API_KEY") ?: "")
200+
inputs.property("TRUST_BASE_PATH", System.getenv("TRUST_BASE_PATH") ?: "")
201+
inputs.property(
202+
"cucumber.filter.tags",
203+
System.getProperty("cucumber.filter.tags") ?: ""
204+
)
205+
206+
// Stream Cucumber's `pretty` formatter output live to the console.
207+
// Without this, Gradle captures the test JVM's stdout and you only see a
208+
// progress bar + pass/fail totals at the end.
209+
testLogging {
210+
showStandardStreams = true
211+
events("passed", "skipped", "failed")
212+
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
213+
}
214+
215+
filter {
216+
includeTestsMatching("*CucumberTestRunner*")
217+
}
218+
shouldRunAfter(tasks.test)
219+
}
220+
221+
// Nametag-gated scenarios: expected to be reported as SKIPPED until UnicityId
222+
// is ported to Java v2. See BDD_MIGRATION_PLAN.md Phase 8.
223+
tasks.register<Test>("bddNametag") {
224+
useJUnitPlatform()
225+
maxHeapSize = "1024m"
226+
systemProperty("cucumber.junit-platform.naming-strategy", "long")
227+
systemProperties = System.getProperties().toMap() as Map<String, Any>
228+
systemProperty("cucumber.filter.tags", "@nametag")
229+
230+
filter {
231+
includeTestsMatching("*CucumberTestRunner*")
232+
}
233+
shouldRunAfter(tasks.test)
234+
}
235+
236+
// Performance / load scenarios — needs sharded aggregator topology.
237+
tasks.register<Test>("bddSlow") {
238+
useJUnitPlatform()
239+
maxHeapSize = "4096m"
240+
systemProperty("cucumber.junit-platform.naming-strategy", "long")
241+
systemProperties = System.getProperties().toMap() as Map<String, Any>
242+
systemProperty("cucumber.filter.tags", "@slow or @shard-load")
243+
244+
filter {
245+
includeTestsMatching("*CucumberTestRunner*")
246+
}
247+
shouldRunAfter(tasks.test)
248+
}
249+
250+
// Fast subset — excludes @tree (the ~136-row 4-level-tree family that dominates
251+
// live-aggregator runtime) in addition to the default exclusions. Use this for
252+
// quick live-aggregator smoke runs. Full coverage: use bddTest (slow on live).
253+
tasks.register<Test>("bddTestFast") {
254+
useJUnitPlatform()
255+
maxHeapSize = "1024m"
256+
systemProperty("cucumber.junit-platform.naming-strategy", "long")
257+
systemProperties = System.getProperties().toMap() as Map<String, Any>
258+
systemProperty(
259+
"cucumber.filter.tags",
260+
"not @nametag and not @slow and not @wip and not @ignore and not @tree"
261+
)
262+
inputs.property("AGGREGATOR_URL", System.getenv("AGGREGATOR_URL") ?: "")
263+
inputs.property("TRUST_BASE_PATH", System.getenv("TRUST_BASE_PATH") ?: "")
264+
testLogging {
265+
showStandardStreams = true
266+
events("passed", "skipped", "failed")
267+
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
268+
}
269+
filter {
270+
includeTestsMatching("*CucumberTestRunner*")
271+
}
272+
shouldRunAfter(tasks.test)
273+
}
274+
275+
// Everything — used for coverage sweeps. Expect nametag scenarios to skip until
276+
// UnicityId is ported.
277+
tasks.register<Test>("bddAll") {
278+
useJUnitPlatform()
279+
maxHeapSize = "2048m"
280+
systemProperty("cucumber.junit-platform.naming-strategy", "long")
281+
systemProperties = System.getProperties().toMap() as Map<String, Any>
282+
systemProperty("cucumber.filter.tags", "not @ignore")
283+
284+
filter {
285+
includeTestsMatching("*CucumberTestRunner*")
286+
}
287+
shouldRunAfter(tasks.test)
288+
}
289+
183290
// Create separate JARs for each platform
184291
tasks.register<Jar>("androidJar") {
185292
archiveClassifier.set("android")
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.unicitylabs.sdk.e2e;
2+
3+
import io.cucumber.junit.platform.engine.Constants;
4+
import org.junit.platform.suite.api.ConfigurationParameter;
5+
import org.junit.platform.suite.api.IncludeEngines;
6+
import org.junit.platform.suite.api.SelectPackages;
7+
import org.junit.platform.suite.api.Suite;
8+
9+
@Suite
10+
@IncludeEngines("cucumber")
11+
@SelectPackages("org.unicitylabs.sdk.features")
12+
@ConfigurationParameter(
13+
key = Constants.GLUE_PROPERTY_NAME,
14+
value = "org.unicitylabs.sdk.e2e.steps,"
15+
+ "org.unicitylabs.sdk.e2e.steps.shared,"
16+
+ "org.unicitylabs.sdk.e2e.config")
17+
@ConfigurationParameter(
18+
key = Constants.PLUGIN_PROPERTY_NAME,
19+
value = "pretty,"
20+
+ "html:build/cucumber-reports/cucumber.html,"
21+
+ "json:build/cucumber-reports/cucumber.json")
22+
@ConfigurationParameter(
23+
key = Constants.EXECUTION_DRY_RUN_PROPERTY_NAME,
24+
value = "false")
25+
@ConfigurationParameter(
26+
key = Constants.PLUGIN_PUBLISH_QUIET_PROPERTY_NAME,
27+
value = "true")
28+
// Default tag filter — excludes nametag-gated scenarios (blocked until UnicityId
29+
// lands), load/perf scenarios, and work-in-progress. Overridable by passing
30+
// -Dcucumber.filter.tags=<expr> on the command line; system properties take
31+
// precedence over @ConfigurationParameter defaults per JUnit Platform spec.
32+
@ConfigurationParameter(
33+
key = Constants.FILTER_TAGS_PROPERTY_NAME,
34+
value = "not @slow and not @wip and not @ignore "
35+
+ "and not @bft-shard-only and not @multi-shard-only "
36+
+ "and not @pending-src-cleanup "
37+
+ "and not @stateful and not @fresh-aggregator")
38+
public class CucumberTestRunner {
39+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.unicitylabs.sdk.e2e.config;
2+
3+
import io.cucumber.java.After;
4+
import io.cucumber.java.Before;
5+
import io.cucumber.java.Scenario;
6+
import org.unicitylabs.sdk.e2e.context.TestContext;
7+
8+
/**
9+
* Cucumber lifecycle hooks. Cucumber's PicoContainer creates one instance per scenario and
10+
* injects the shared {@link TestContext}, ensuring the same context reaches every step class
11+
* that takes it as a constructor parameter.
12+
*/
13+
public class CucumberConfiguration {
14+
15+
private final TestContext testContext;
16+
17+
public CucumberConfiguration(TestContext testContext) {
18+
this.testContext = testContext;
19+
}
20+
21+
@Before
22+
public void beforeScenario(Scenario scenario) {
23+
testContext.clearTestState();
24+
System.out.println("[BDD] >>> " + scenario.getName());
25+
}
26+
27+
@After
28+
public void afterScenario(Scenario scenario) {
29+
System.out.println(
30+
"[BDD] <<< " + scenario.getName() + " — " + scenario.getStatus());
31+
}
32+
33+
@After("@reset")
34+
public void fullReset() {
35+
testContext.reset();
36+
}
37+
}

0 commit comments

Comments
 (0)