You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2026/07/23 16:29:29 UTC
(struts) 02/06: WW-5462 docs: add implementation plan and refine spec with build details
This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch WW-5462-mock-package-cleanup
in repository https://gitbox.apache.org/repos/asf/struts.git
commit e333c1898e999028c472b5792314e6df9b85daa1
Author: Lukasz Lenart <lu...@apache.org>
AuthorDate: Thu Jul 23 16:43:44 2026 +0200
WW-5462 docs: add implementation plan and refine spec with build details
Co-Authored-By: Claude Opus 4.8 <no...@anthropic.com>
---
.../2026-07-23-WW-5462-mock-package-test-jar.md | 285 +++++++++++++++++++++
...6-07-23-WW-5462-mock-package-test-jar-design.md | 12 +
2 files changed, 297 insertions(+)
diff --git a/docs/superpowers/plans/2026-07-23-WW-5462-mock-package-test-jar.md b/docs/superpowers/plans/2026-07-23-WW-5462-mock-package-test-jar.md
new file mode 100644
index 000000000..ed5923433
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-23-WW-5462-mock-package-test-jar.md
@@ -0,0 +1,285 @@
+# WW-5462 Mock Package Test-Jar Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Remove the test-only `org.apache.struts2.mock` package from the published `struts2-core` jar by moving it to test sources and sharing it with plugin tests via a filtered test-jar.
+
+**Architecture:** Core attaches a `maven-jar-plugin` test-jar containing only `org/apache/struts2/mock/**`. Six plugins consume it as a test-scoped `test-jar` dependency. Tasks are ordered so every commit leaves the full reactor green: pom plumbing first (mocks still in main sources), the move last.
+
+**Tech Stack:** Java 17, Maven multi-module reactor, maven-jar-plugin `test-jar` goal.
+
+**Spec:** `docs/superpowers/specs/2026-07-23-WW-5462-mock-package-test-jar-design.md`
+
+## Global Constraints
+
+- Branch: `WW-5462-mock-package-cleanup` (already exists; work there, never on `main`)
+- Every commit message starts with `WW-5462` followed by conventional-commit type (see `~/.claude/commit_guideline.md`)
+- Package name `org.apache.struts2.mock` never changes — no import edits anywhere
+- The bom (`bom/pom.xml`) must NOT gain a test-jar entry
+- Test-jar includes filter is exactly `org/apache/struts2/mock/**`
+- No new files other than pom edits; this is a move/delete change
+
+## Known Risk (check in Task 2, Step 3)
+
+When the reactor runs without packaging (`mvn test`), Maven's `ReactorReader` resolves the
+test-jar dependency to core's whole `target/test-classes` directory — the includes filter
+applies only when the jar is physically packaged. Core's test resources (`struts.xml`,
+`struts.properties`, `struts-tests-default.xml`, `log4j2.xml`) then appear on plugin test
+classpaths. Plugins `json`, `rest`, `xslt`, `jasperreports` have no own root `struts.xml`
+to shadow core's. If Task 2's verification shows plugin test failures caused by leaked core
+test resources, STOP and report back — the fallback options (renaming core's clashing test
+resources, or copying mocks into plugins instead of the test-jar) need a human decision.
+
+---
+
+### Task 1: Attach filtered test-jar in core
+
+**Files:**
+- Modify: `core/pom.xml` (the `<build><plugins>` section starting at line 39)
+
+**Interfaces:**
+- Consumes: nothing from other tasks
+- Produces: attached artifact `org.apache.struts:struts2-core:test-jar` (classifier `tests`), containing only `org/apache/struts2/mock/**`. Task 2 depends on these exact coordinates.
+
+Background for the implementer: the root `pom.xml` (line ~467) configures `maven-jar-plugin`
+with `<archive><manifestFile>` pointing at the OSGi manifest that the felix bundle plugin
+generates for the **main** jar. The test-jar must not inherit that manifest, hence the
+`<archive combine.self="override"/>` below, which resets the inherited `<archive>` config
+so the test-jar gets a default manifest.
+
+- [ ] **Step 1: Add the test-jar execution**
+
+In `core/pom.xml`, inside `<build><plugins>`, add as a new `<plugin>` entry directly after the closing tag of the `maven-surefire-plugin` entry (after line 68):
+
+```xml
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>mock-test-jar</id>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ <configuration>
+ <archive combine.self="override"/>
+ <includes>
+ <include>org/apache/struts2/mock/**</include>
+ </includes>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+```
+
+- [ ] **Step 2: Package core and verify the build passes**
+
+Run:
+```bash
+mvn package -DskipTests -DskipAssembly -pl core
+```
+Expected: `BUILD SUCCESS`, and the log contains `maven-jar-plugin:...:test-jar (mock-test-jar) @ struts2-core` followed by `Building jar: .../core/target/struts2-core-7.2.0-SNAPSHOT-tests.jar`.
+
+- [ ] **Step 3: Verify test-jar contents**
+
+Run:
+```bash
+unzip -l core/target/struts2-core-*-tests.jar
+```
+Expected: entries only under `META-INF/` and `org/apache/struts2/mock/`. At this point the mock package in test sources holds exactly 3 classes, so the class list is:
+`DummyTextProvider.class`, `InjectableAction.class`, `MockLazyInterceptor.class` (plus any inner/anonymous class files of these). NO other packages, NO `struts.xml`, NO `*.properties`.
+
+- [ ] **Step 4: Verify the test-jar manifest is not the OSGi bundle manifest**
+
+Run:
+```bash
+unzip -p core/target/struts2-core-*-tests.jar META-INF/MANIFEST.MF | head -20
+```
+Expected: a plain default manifest (`Manifest-Version`, `Created-By`, `Build-Jdk-Spec`). It must NOT contain `Export-Package` or `Bundle-SymbolicName`.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add core/pom.xml
+git commit -m "WW-5462 build(core): attach test-jar with the mock package"
+```
+
+---
+
+### Task 2: Wire the six consuming plugins
+
+**Files:**
+- Modify: `plugins/json/pom.xml`
+- Modify: `plugins/rest/pom.xml`
+- Modify: `plugins/spring/pom.xml`
+- Modify: `plugins/xslt/pom.xml`
+- Modify: `plugins/jasperreports/pom.xml`
+- Modify: `plugins/jasperreports7/pom.xml`
+
+**Interfaces:**
+- Consumes: the `struts2-core` test-jar artifact attached in Task 1
+- Produces: plugin test classpaths that resolve `org.apache.struts2.mock` from the test-jar; Task 3 relies on this to move the mocks without breaking plugin builds
+
+- [ ] **Step 1: Add the test-jar dependency to all six plugin poms**
+
+In each of the six poms listed above, inside the `<dependencies>` section, add this block immediately before the first `<scope>test</scope>` dependency (each pom has a `<!-- Test dependencies -->`-style boundary; json's is at line 63 — place it right after that comment where present, otherwise before the junit dependency):
+
+```xml
+ <dependency>
+ <groupId>org.apache.struts</groupId>
+ <artifactId>struts2-core</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+```
+
+The block is identical in all six poms. `${project.version}` is correct because all modules share the reactor version; the bom must not be touched.
+
+- [ ] **Step 2: Verify the six plugins build and pass with the new dependency**
+
+Run:
+```bash
+mvn test -DskipAssembly -am -pl plugins/json,plugins/rest,plugins/spring,plugins/xslt,plugins/jasperreports,plugins/jasperreports7
+```
+Expected: `BUILD SUCCESS`; all plugin tests pass. The mocks still resolve from core's main classes at this point, so this proves only that the added dependency is well-formed and harmless.
+
+- [ ] **Step 3: Check for the known risk — core test-resource leakage**
+
+The Step 2 run used unpackaged reactor resolution, i.e. plugin test classpaths contained core's whole `target/test-classes` (see "Known Risk" above). Examine the Step 2 output:
+- All tests pass → risk is benign, continue.
+- Any plugin test failure that does not occur on `main` → STOP. Re-run that plugin's tests on `main` to confirm the regression is caused by this change, then report back with the failing tests and the leaked-resource explanation. Do not improvise a fix.
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add plugins/json/pom.xml plugins/rest/pom.xml plugins/spring/pom.xml plugins/xslt/pom.xml plugins/jasperreports/pom.xml plugins/jasperreports7/pom.xml
+git commit -m "WW-5462 build(plugins): consume struts2-core test-jar in tests"
+```
+
+---
+
+### Task 3: Delete MockContainer, move the five remaining mocks
+
+**Files:**
+- Delete: `core/src/main/java/org/apache/struts2/mock/MockContainer.java`
+- Move: `core/src/main/java/org/apache/struts2/mock/MockActionInvocation.java` → `core/src/test/java/org/apache/struts2/mock/MockActionInvocation.java`
+- Move: `core/src/main/java/org/apache/struts2/mock/MockActionProxy.java` → `core/src/test/java/org/apache/struts2/mock/MockActionProxy.java`
+- Move: `core/src/main/java/org/apache/struts2/mock/MockInterceptor.java` → `core/src/test/java/org/apache/struts2/mock/MockInterceptor.java`
+- Move: `core/src/main/java/org/apache/struts2/mock/MockObjectTypeDeterminer.java` → `core/src/test/java/org/apache/struts2/mock/MockObjectTypeDeterminer.java`
+- Move: `core/src/main/java/org/apache/struts2/mock/MockResult.java` → `core/src/test/java/org/apache/struts2/mock/MockResult.java`
+
+**Interfaces:**
+- Consumes: test-jar packaging (Task 1) and plugin wiring (Task 2) — both must be committed first, or this task breaks plugin builds
+- Produces: `struts2-core` main jar without `org.apache.struts2.mock`; test-jar now carries all 8 mock classes
+
+- [ ] **Step 1: Delete the dead class and move the five live ones**
+
+```bash
+git rm core/src/main/java/org/apache/struts2/mock/MockContainer.java
+git mv core/src/main/java/org/apache/struts2/mock/MockActionInvocation.java core/src/test/java/org/apache/struts2/mock/
+git mv core/src/main/java/org/apache/struts2/mock/MockActionProxy.java core/src/test/java/org/apache/struts2/mock/
+git mv core/src/main/java/org/apache/struts2/mock/MockInterceptor.java core/src/test/java/org/apache/struts2/mock/
+git mv core/src/main/java/org/apache/struts2/mock/MockObjectTypeDeterminer.java core/src/test/java/org/apache/struts2/mock/
+git mv core/src/main/java/org/apache/struts2/mock/MockResult.java core/src/test/java/org/apache/struts2/mock/
+```
+
+Then confirm the main-side package directory is gone:
+```bash
+ls core/src/main/java/org/apache/struts2/mock/ 2>&1
+```
+Expected: `No such file or directory`.
+
+No source edits: the package declaration stays `org.apache.struts2.mock`, so no imports change anywhere.
+
+- [ ] **Step 2: Run core tests**
+
+Run:
+```bash
+mvn test -DskipAssembly -pl core
+```
+Expected: `BUILD SUCCESS`, same test count as on `main` (the ~50 core test classes using the mocks compile against test sources now).
+
+- [ ] **Step 3: Verify jar contents after the move**
+
+Run:
+```bash
+mvn package -DskipTests -DskipAssembly -pl core
+unzip -l core/target/struts2-core-7.2.0-SNAPSHOT.jar | grep 'struts2/mock' ; echo "main-jar grep exit: $?"
+unzip -l core/target/struts2-core-7.2.0-SNAPSHOT-tests.jar | grep -c 'struts2/mock/.*\.class'
+```
+Expected: main-jar grep exit code `1` (no mock package in the published jar); tests-jar contains `8` classes — the 5 moved + `DummyTextProvider`, `InjectableAction`, `MockLazyInterceptor` (count may exceed 8 only if inner-class files exist; there must be exactly 8 top-level `*.class` names).
+
+- [ ] **Step 4: Verify plugin tests still pass against the moved mocks**
+
+Run:
+```bash
+mvn test -DskipAssembly -am -pl plugins/json,plugins/rest,plugins/spring,plugins/xslt,plugins/jasperreports,plugins/jasperreports7
+```
+Expected: `BUILD SUCCESS`, all tests pass — mocks now come from core's test classpath contribution.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add -A core/src
+git commit -m "WW-5462 refactor(core): move mock package to test sources, drop unused MockContainer
+
+BREAKING CHANGE: org.apache.struts2.mock is no longer part of the
+published struts2-core jar. Downstream tests should use
+struts2-junit-plugin, Mockito, or a local copy of the needed mock."
+```
+
+---
+
+### Task 4: Remove spring's duplicate mock and run full verification
+
+**Files:**
+- Delete: `plugins/spring/src/test/java/org/apache/struts2/mock/DummyTextProvider.java`
+
+**Interfaces:**
+- Consumes: test-jar wiring from Task 2 (spring resolves `DummyTextProvider` from core's test-jar instead of its local copy)
+- Produces: final verified state of the branch
+
+- [ ] **Step 1: Delete the duplicate**
+
+```bash
+git rm plugins/spring/src/test/java/org/apache/struts2/mock/DummyTextProvider.java
+```
+
+Same package and class name ship in the core test-jar, so `plugins/spring/src/test/java/org/apache/struts2/spring/SpringObjectFactoryTest.java` (its only consumer, import at line 39) needs no edit.
+
+- [ ] **Step 2: Run spring plugin tests**
+
+Run:
+```bash
+mvn test -DskipAssembly -am -pl plugins/spring
+```
+Expected: `BUILD SUCCESS`, all tests pass.
+
+- [ ] **Step 3: Full reactor test run**
+
+Run:
+```bash
+mvn test -DskipAssembly
+```
+Expected: `BUILD SUCCESS` — every module compiles and passes.
+
+- [ ] **Step 4: Jakarta EE 11 profile sanity check**
+
+Run:
+```bash
+mvn clean install -Pjakartaee11 -DskipAssembly
+```
+Expected: `BUILD SUCCESS`. (This also re-verifies the packaged-jar path: with `install`, plugins resolve the real filtered test-jar, not the test-classes directory.)
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add -A plugins/spring
+git commit -m "WW-5462 test(spring): use DummyTextProvider from core test-jar"
+```
+
+- [ ] **Step 6: Remind the user about the release-notes migration note**
+
+Tell the user (do not attempt to do this yourself — it is a Jira/Confluence action, not a repo change):
+> The 7.3.0 release notes need a migration note: `org.apache.struts2.mock` was removed from the struts2-core jar (WW-5462); downstream tests should switch to struts2-junit-plugin, Mockito, or copy the needed mock class.
diff --git a/docs/superpowers/specs/2026-07-23-WW-5462-mock-package-test-jar-design.md b/docs/superpowers/specs/2026-07-23-WW-5462-mock-package-test-jar-design.md
index 1c697c6d0..48175aad0 100644
--- a/docs/superpowers/specs/2026-07-23-WW-5462-mock-package-test-jar-design.md
+++ b/docs/superpowers/specs/2026-07-23-WW-5462-mock-package-test-jar-design.md
@@ -57,6 +57,7 @@ In `core/pom.xml`, add a `maven-jar-plugin` execution:
<goal>test-jar</goal>
</goals>
<configuration>
+ <archive combine.self="override"/>
<includes>
<include>org/apache/struts2/mock/**</include>
</includes>
@@ -64,6 +65,17 @@ In `core/pom.xml`, add a `maven-jar-plugin` execution:
</execution>
```
+The `<archive combine.self="override"/>` resets the `<archive><manifestFile>` configuration
+inherited from the root pom, which points at the OSGi manifest generated for the main jar;
+without it the test-jar would carry the main bundle's manifest.
+
+**Dev-build caveat:** when the reactor runs without packaging (`mvn test -DskipAssembly`),
+Maven resolves the test-jar dependency to core's whole `target/test-classes` directory —
+the includes filter applies only to the packaged jar. Plugin test classpaths then also see
+core's other test classes and test resources (`struts.xml`, `struts.properties`). This is
+verified empirically during implementation; packaged builds (`mvn package`/`install`) always
+use the filtered jar.
+
The includes filter is essential: it keeps core's other test classes and test resources
(`struts.xml` variants, test properties) off plugin test classpaths, where they would
conflict with plugin test configuration. The resulting