Skip to content

test: add per-platform startup graph regression tests#66

Merged
robinbraemer merged 4 commits into
mainfrom
fm/connect-java-per-platform-startup-tests-p2
Jul 25, 2026
Merged

test: add per-platform startup graph regression tests#66
robinbraemer merged 4 commits into
mainfrom
fm/connect-java-per-platform-startup-tests-p2

Conversation

@robinbraemer

Copy link
Copy Markdown
Member

Intent

Add durable CI tests that boot/start the Connect plugin on EVERY supported platform (Velocity, Spigot/Bukkit/Paper, BungeeCord — enumerated from the repo's platform modules; there is no Fabric module so it is intentionally not included) and assert the plugin's DI graph provisions and initializes cleanly, so a startup/DI regression is caught in CI instead of by users. This is the captain-directed prevention for the class of bug that produced BOTH the Velocity 4.0.0 Guice-7 DI failure (BedrockIdentityKeyProvider/BedrockAdmissionCoordinator unprovisionable because they were annotated with javax.inject, which Velocity 4's Guice 7 ignores) AND the Java-26 Libp2pEndpointRuntime reflective-constructor arity failure.

Design decisions/tradeoffs made: (1) A full real boot of ConnectPlatform is deliberately NOT driven because ConnectPlatform.init() loads config via EndpointNameGenerator, which makes a network call — not hermetic. Instead each test provisions the real Guice module graph up to the plugin's key singletons. (2) This repo builds/tests against Guice 6, which accepts BOTH javax.inject and com.google.inject, so a real Guice-6 provision cannot reproduce the Velocity-4 failure; the shared testFixtures helper (core/src/testFixtures StartupGraphProvisioning) instead replays Guice 7's exact injectable-constructor discovery rule + a javax.inject scan across the reflectively-walked @Inject graph — this is what fails pre-fix and passes post-fix, and it generalizes the existing BedrockVelocityGuice7ProvisioningTest from a hand-listed set to the whole per-platform graph. (3) Spigot/Bungee plugin entrypoints extend Bukkit JavaPlugin / Bungee Plugin and cannot be instantiated off-server, so their real-provision half wires Server/ProxyCommonModule with platform-supplied bindings stubbed (documented limitation) — still provisioning the full Connect object graph a DI regression would break. Velocity's entrypoint takes an Injector so its child injector is booted for real. (4) I verified the required property by reverse-applying the #64 fix (restoring javax) and confirming the Velocity + core tests fail naming BedrockIdentityKeyProvider, then restoring the fix so they pass. (5) The Java-26 reflective-arity regression class stays guarded by the existing signature-level tests (Libp2pEndpointRuntimeInitTest/Libp2pRuntimeBoundaryTest), which are JDK-independent and referenced in the new tests' docs. (6) CI: pullrequest.yml now runs ./gradlew build on a JDK 17/21 matrix (artifacts only from 17); it stops at 21 because Gradle 8.5 cannot run on JDK 26, with rationale documented. New tests run on every PR via the existing build task.

Also added test infrastructure to the bungee module (it previously had no tests) and wired core java-test-fixtures consumed by the platform modules. AGENTS.md/CLAUDE.md updated with a concise pointer to the guard.

What Changed

  • Added shared core startup-graph provisioning that checks Guice 7 constructor rules and scans the reachable @Inject graph for javax.inject regressions.
  • Added startup/DI tests for Velocity, Spigot/Bukkit/Paper, and BungeeCord, with hermetic host-platform stubs where needed, plus Bungee test infrastructure and shared fixture wiring.
  • Updated PR CI for full Gradle builds on JDK 17/21 and documented the startup/DI and reflective-constructor guards.

Risk Assessment

✅ Low: The change is bounded to CI and test infrastructure and covers the previously identified startup graph gaps without altering production behavior.

Testing

No baseline test command was provided. On JDK 21 with Gradle 8.5, the targeted Velocity, Spigot, Bungee, shared graph, libp2p reflective-boundary, and Bedrock Guice-7 tests executed successfully; reviewer-visible result extracts were saved in the required evidence directory, and the worktree remains clean.

Evidence: Targeted JUnit results
bungee/build/test-results/test/TEST-com.minekube.connect.BungeePluginStartupTest.xml:2:<testsuite name="com.minekube.connect.BungeePluginStartupTest" tests="2" skipped="0" failures="0" errors="0" timestamp="2026-07-25T06:53:26" hostname="Robins-MacBook-Pro.fritz.box" time="0.425">
bungee/build/test-results/test/TEST-com.minekube.connect.BungeePluginStartupTest.xml:4:  <testcase name="bungeePluginGraphIsProvisionableUnderGuice7()" classname="com.minekube.connect.BungeePluginStartupTest" time="0.042"/>
bungee/build/test-results/test/TEST-com.minekube.connect.BungeePluginStartupTest.xml:5:  <testcase name="bungeeProxyGraphProvisionsKeySingletonsWithoutError()" classname="com.minekube.connect.BungeePluginStartupTest" time="0.381"/>
spigot/build/test-results/test/TEST-com.minekube.connect.SpigotPluginStartupTest.xml:2:<testsuite name="com.minekube.connect.SpigotPluginStartupTest" tests="2" skipped="0" failures="0" errors="0" timestamp="2026-07-25T06:53:26" hostname="Robins-MacBook-Pro.fritz.box" time="0.325">
spigot/build/test-results/test/TEST-com.minekube.connect.SpigotPluginStartupTest.xml:4:  <testcase name="spigotPluginGraphIsProvisionableUnderGuice7()" classname="com.minekube.connect.SpigotPluginStartupTest" time="0.03"/>
spigot/build/test-results/test/TEST-com.minekube.connect.SpigotPluginStartupTest.xml:5:  <testcase name="spigotServerGraphProvisionsKeySingletonsWithoutError()" classname="com.minekube.connect.SpigotPluginStartupTest" time="0.294"/>
core/build/test-results/test/TEST-com.minekube.connect.tunnel.p2p.Libp2pEndpointRuntimeInitTest.xml:2:<testsuite name="com.minekube.connect.tunnel.p2p.Libp2pEndpointRuntimeInitTest" tests="1" skipped="0" failures="0" errors="0" timestamp="2026-07-25T06:53:18" hostname="Robins-MacBook-Pro.fritz.box" time="0.011">
core/build/test-results/test/TEST-com.minekube.connect.tunnel.p2p.Libp2pEndpointRuntimeInitTest.xml:4:  <testcase name="initializesRuntimeAcrossIsolatedLoaderBoundary(Path)" classname="com.minekube.connect.tunnel.p2p.Libp2pEndpointRuntimeInitTest" time="0.011"/>
velocity/build/test-results/test/TEST-com.minekube.connect.VelocityPluginStartupTest.xml:2:<testsuite name="com.minekube.connect.VelocityPluginStartupTest" tests="2" skipped="0" failures="0" errors="0" timestamp="2026-07-25T06:53:24" hostname="Robins-MacBook-Pro.fritz.box" time="0.517">
velocity/build/test-results/test/TEST-com.minekube.connect.VelocityPluginStartupTest.xml:4:  <testcase name="velocityPluginGraphIsProvisionableUnderGuice7()" classname="com.minekube.connect.VelocityPluginStartupTest" time="0.031"/>
velocity/build/test-results/test/TEST-com.minekube.connect.VelocityPluginStartupTest.xml:5:  <testcase name="velocityModuleGraphProvisionsKeySingletonsWithoutError()" classname="com.minekube.connect.VelocityPluginStartupTest" time="0.485"/>
core/build/test-results/test/TEST-com.minekube.connect.tunnel.p2p.Libp2pRuntimeBoundaryTest.xml:2:<testsuite name="com.minekube.connect.tunnel.p2p.Libp2pRuntimeBoundaryTest" tests="1" skipped="0" failures="0" errors="0" timestamp="2026-07-25T06:53:18" hostname="Robins-MacBook-Pro.fritz.box" time="0.001">
core/build/test-results/test/TEST-com.minekube.connect.tunnel.p2p.Libp2pRuntimeBoundaryTest.xml:4:  <testcase name="parentFacingEndpointDoesNotHoldIsolatedRuntimeTypes()" classname="com.minekube.connect.tunnel.p2p.Libp2pRuntimeBoundaryTest" time="0.001"/>
core/build/test-results/test/TEST-com.minekube.connect.startup.PluginGraphStartupTest.xml:2:<testsuite name="com.minekube.connect.startup.PluginGraphStartupTest" tests="3" skipped="0" failures="0" errors="0" timestamp="2026-07-25T06:53:18" hostname="Robins-MacBook-Pro.fritz.box" time="0.302">
core/build/test-results/test/TEST-com.minekube.connect.startup.PluginGraphStartupTest.xml:4:  <testcase name="sharedRuntimeGraphIsProvisionableUnderGuice7()" classname="com.minekube.connect.startup.PluginGraphStartupTest" time="0.022"/>
core/build/test-results/test/TEST-com.minekube.connect.startup.PluginGraphStartupTest.xml:5:  <testcase name="reflectiveWalkReachesTheClassesThatRegressed()" classname="com.minekube.connect.startup.PluginGraphStartupTest" time="0.001"/>
core/build/test-results/test/TEST-com.minekube.connect.startup.PluginGraphStartupTest.xml:6:  <testcase name="serverGraphProvisionsKeySingletonsWithoutError()" classname="com.minekube.connect.startup.PluginGraphStartupTest" time="0.278"/>
Evidence: Bedrock Guice-7 results
2:<testsuite name="com.minekube.connect.bedrock.BedrockVelocityGuice7ProvisioningTest" tests="4" skipped="0" failures="0" errors="0" timestamp="2026-07-25T06:54:49" hostname="Robins-MacBook-Pro.fritz.box" time="0.266">
4:  <testcase name="bedrockGraphResolvesThroughRealGuiceInjector()" classname="com.minekube.connect.bedrock.BedrockVelocityGuice7ProvisioningTest" time="0.259"/>
5:  <testcase name="bedrockAdmissionCoordinatorIsProvisionableUnderGuice7()" classname="com.minekube.connect.bedrock.BedrockVelocityGuice7ProvisioningTest" time="0.003"/>
6:  <testcase name="connectDiClassesCarryNoJavaxInjectAnnotations()" classname="com.minekube.connect.bedrock.BedrockVelocityGuice7ProvisioningTest" time="0.002"/>
7:  <testcase name="bedrockIdentityKeyProviderIsProvisionableUnderGuice7()" classname="com.minekube.connect.bedrock.BedrockVelocityGuice7ProvisioningTest" time="0.0"/>

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (3) ✅
  • ⚠️ core/src/testFixtures/java/com/minekube/connect/startup/StartupGraphProvisioning.java:156 - Field/method-injected roots are excluded from the checked set: WatcherRegister, VelocityListener, BungeeListener, and SpigotDataAddon have no injectable constructor, so they are only enqueued, not discovered. The later early return for types without an injectable constructor also skips their javax.inject member scan. A Guice-7 regression on one of these active startup members could therefore pass; include member-injected roots and scan their annotations.
  • ⚠️ velocity/src/test/java/com/minekube/connect/VelocityPluginStartupTest.java:127 - The intent requires durable tests for the plugin DI graph on every supported platform, but the real provisioning only creates the common module graph and resolves selected keys; it does not install the platform enable modules or follow explicit interface bindings. For example, VelocityPlatformModule's VelocityCommandUtil/VelocityPlatformUtils and VelocityListenerModule's ListenerRegister are not exercised, with analogous omissions in Spigot/Bungee. A startup DI regression in those concrete platform bindings can pass all new tests. Please confirm whether this narrower key-singleton slice is intentional or expand the graph roots/provisioning.

🔧 Fix: Expand startup graph coverage for member-injected platform bindings
1 warning still open:

  • ⚠️ spigot/src/test/java/com/minekube/connect/SpigotPluginStartupTest.java:83 - SpigotPlugin.onEnable() installs SpigotAddonModule, whose providers create AddonManagerAddon, DebugAddon, and PacketHandlerAddon; AddonRegister then injects members into every addon. The roots include only SpigotDataAddon, so Guice-7/member-injection regressions in those three real startup classes are still invisible. Add them to spigotGraphRoots() and assert the walk reaches them.

🔧 Fix: Cover Spigot addon bindings in startup graph tests
1 warning still open:

  • ⚠️ core/src/testFixtures/java/com/minekube/connect/startup/StartupGraphProvisioning.java:117 - The shared roots claim to cover startup singletons, but omit CommandRegister and ListenerRegister, which are eager singletons installed by every platform's enable path; AddonRegister is likewise eager in SpigotAddonModule. Because the real tests do not install those enable modules, the reflective walk never checks their @Inject constructors/methods. A Guice-7/javax.inject regression in registrar initialization can still pass. Add these registrar classes to the shared/platform roots or provision the enable modules with hermetic stubs.

🔧 Fix: Cover enable-time registrars in startup graph tests
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • ./gradlew --no-daemon --console=plain :core:test --tests com.minekube.connect.startup.PluginGraphStartupTest --tests com.minekube.connect.tunnel.p2p.Libp2pEndpointRuntimeInitTest --tests com.minekube.connect.tunnel.p2p.Libp2pRuntimeBoundaryTest
  • ./gradlew --no-daemon --console=plain :velocity:test --tests com.minekube.connect.VelocityPluginStartupTest
  • ./gradlew --no-daemon --console=plain :spigot:test --tests com.minekube.connect.SpigotPluginStartupTest
  • ./gradlew --no-daemon --console=plain :bungee:test --tests com.minekube.connect.BungeePluginStartupTest
  • ./gradlew --no-daemon --console=plain :core:test --tests com.minekube.connect.bedrock.BedrockVelocityGuice7ProvisioningTest
  • JUnit XML inspection confirming selected test methods executed with zero failures/errors
  • Working-tree and evidence cleanup verification
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Add plugin startup/smoke tests for every supported Connect platform
(Velocity, Spigot, Bungee) plus a core shared-graph test, so a
startup/DI-provisioning regression is caught in CI instead of by users.
This is the prevention for the class of bug behind BOTH the Velocity
4.0.0 Guice 7 failure (BedrockIdentityKeyProvider unprovisionable) and
the Java-26 Libp2pEndpointRuntime constructor arity failure.

- core testFixtures StartupGraphProvisioning: pure-JDK reflective
  @Inject-graph walker + a Guice 7 injectable-constructor rule replica +
  javax.inject scan, generalizing BedrockVelocityGuice7ProvisioningTest to
  the whole per-platform graph so new DI classes are covered automatically.
- Per-platform tests provision each platform's real Guice module graph
  (Velocity boots the real ProxyCommonModule + VelocityPlatformModule child
  injector; Spigot/Bungee wire Server/ProxyCommonModule with platform SDK
  stubbed, documented) and assert Guice-7 provisionability. The Velocity
  test fails on the pre-fix javax annotations and passes on the fix.
- Java-26 reflective-arity class stays guarded by the existing signature
  tests (Libp2pEndpointRuntimeInitTest / Libp2pRuntimeBoundaryTest).
- CI: pullrequest.yml runs ./gradlew build on a JDK 17/21 matrix; artifacts
  from 17. Rationale documented (Gradle 8.5 caps the runnable JDK).
@robinbraemer
robinbraemer merged commit 8616816 into main Jul 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant