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 <[email protected]>
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 <[email protected]>
---
 .../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

Reply via email to