This is an automated email from the ASF dual-hosted git repository.
ashishvijaywargiya pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/trunk by this push:
new b9dfe95aa1 Fix webtools Run Suite/Run Case: missing prerequisites and
shared ambient transaction (#1655)
b9dfe95aa1 is described below
commit b9dfe95aa10b2857110d6d7c0db2a689d0d1fb65
Author: Ashish Vijaywargiya <[email protected]>
AuthorDate: Sat Aug 15 12:30:34 2026 +0530
Fix webtools Run Suite/Run Case: missing prerequisites and shared ambient
transaction (#1655)
## Problem
Webtools' "Run Suite" / "Run Case" (`TestSuiteInfo` ->
`RunTest/{compName}/{suiteName}[/{caseName}]`) had two bugs that didn't
show up via the CLI's `ofbiz --test`:
1. Requesting a single case (`case=`) ran only that exact case, silently
skipping sibling setup cases it depends on (e.g. `party-tests` needs the
data `party-tests-data-load` creates), so a filtered run could fail for
missing data that the full suite run never exposed.
2. `entity`/`service`/`ecommerce` full-suite runs failed or misbehaved:
`RunTestEvents` -> `TestRunContainer` runs a whole testdef suite's worth
of otherwise-independent test-cases inside one
`JavaEventHandler`-invoked event call, and `JavaEventHandler`
unconditionally wraps that whole call in one ambient transaction. A
`require-new-transaction` service invoked by one test-case couldn't see
data a prior test-case's own entity-xml load had "committed" (that
commit was a no-op participant in the still-open ambient transaction),
and one test-case marking that shared transaction rollback-only poisoned
every test-case after it in the same run. Neither symptom is possible
via `ofbiz --test`, which runs `TestRunContainer` as a bare `Container`
with no event/HTTP layer and no ambient transaction at all.
## Fix
**`ModelTestSuite`**: `case=` filtering now auto-includes only genuine
data-load prerequisites (`entity-xml`/`entity-xml-assert` with
`action="load"`) declared before the requested case - not every
preceding case. An earlier, broader version of this fix (running
everything declared before the target) over-included independent sibling
test classes, real `service-test` cases, and `entity-xml
action="assert"` checks - `entitytests.xml` bundles five unrelated
`jupiter-test-suite` classes in one file, and requesting one used to
also run the others, including a deliberately slow one. Covered by unit
tests in `ModelTestSuiteTest`.
**`JavaEventHandler`**: honors the existing `global-transaction="false"`
`<event>` attribute (previously only read by `ServiceMultiEventHandler`,
unused anywhere in the codebase until this change).
**`controller.xml`**: declares `global-transaction="false"` on both
`RunTest` request-maps in webtools, opting only that one event out of
the default whole-invocation ambient transaction.
## Testing
- New unit tests in `ModelTestSuiteTest` (case-filtering logic).
- `./gradlew test checkstyleMain checkstyleTest codenarcMain
codenarcTest verifyTestdefClassNames verifyNoBareJupiterExtendWith` -
all clean.
- `./gradlew loadAll testIntegration` (full CLI run, every testdef suite
in the repo): 43 suites, 652 tests, one pre-existing unrelated failure
(`facilitytests`/`InventoryTests.testGetInventoryAvailableByFacility`)
reproduced identically on `trunk` with none of this PR's changes,
confirming it predates this PR.
- Manually exercised `party`, `entity`, `service`, `ecommerce` through
the actual webtools UI (`RunTest/...` URLs) before and after each change
to confirm the fix end to end, not just via the CLI.
---
build.gradle | 11 +-
.../org/apache/ofbiz/testtools/ModelTestSuite.java | 109 ++++++++++++--
.../apache/ofbiz/testtools/ModelTestSuiteTest.java | 160 +++++++++++++++++++++
.../ofbiz/webapp/event/JavaEventHandler.java | 18 ++-
.../webapp/webtools/WEB-INF/controller.xml | 18 ++-
5 files changed, 299 insertions(+), 17 deletions(-)
diff --git a/build.gradle b/build.gradle
index 587a87a84f..1b191128c0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1396,8 +1396,15 @@ tasks.addRule('Pattern: ofbizBackground <Commands>:
Execute OFBiz startup comman
def createOfbizCommandTask(taskName, arguments) {
task(type: JavaExec, dependsOn: classes, taskName) {
jvmArgs(application.applicationDefaultJvmArgs)
+ // Every ofbiz-launched dev server needs test classes on its
classpath, not just one
+ // started with --test/-t: webtools' "Run Test" screen (RunTestEvents
-> TestRunContainer)
+ // loads jupiter-test-suite/junit-test-suite classes by name inside
whatever JVM is already
+ // running, regardless of how that JVM was started. OFBIZ-13402 moved
those classes out of
+ // src/main/* (and so out of sourceSets.main) to keep them out of
release artifacts; this
+ // task is a local dev-run launcher, never a release-packaging task,
so widening its
+ // classpath here doesn't reintroduce test code into anything that
gets distributed.
+ classpath = sourceSets.main.runtimeClasspath +
sourceSets.test.runtimeClasspath
if (taskName ==~ /^ofbiz.*(--test|-t).*/) {
- classpath = sourceSets.main.runtimeClasspath +
sourceSets.test.runtimeClasspath
// TestRunContainer.java writes one XML per suite as
"<suite.getName()>.xml" and only
// ever overwrites the suites a run actually re-executes - nothing
here deletes the
// rest first. Left alone, a narrower run (suitename=... filtering
to one component) or
@@ -1412,8 +1419,6 @@ def createOfbizCommandTask(taskName, arguments) {
}
finalizedBy(createTestReport)
finalizedBy(createFramedTestReport)
- } else {
- classpath = sourceSets.main.runtimeClasspath
}
mainClass = application.mainClass
args arguments
diff --git
a/framework/testtools/src/main/java/org/apache/ofbiz/testtools/ModelTestSuite.java
b/framework/testtools/src/main/java/org/apache/ofbiz/testtools/ModelTestSuite.java
index 9cfdddcbdc..b34a3d11be 100644
---
a/framework/testtools/src/main/java/org/apache/ofbiz/testtools/ModelTestSuite.java
+++
b/framework/testtools/src/main/java/org/apache/ofbiz/testtools/ModelTestSuite.java
@@ -63,23 +63,112 @@ public class ModelTestSuite {
this.delegator =
DelegatorFactory.getDelegator(DELEGATOR_NAME).makeTestDelegator(DELEGATOR_NAME
+ uniqueSuffix);
this.dispatcher = ServiceContainer.getLocalDispatcher(DISPATCHER_NAME
+ uniqueSuffix, delegator);
- for (Element testCaseElement : UtilXml.childElementList(mainElement,
UtilMisc.toSet("test-case", "test-group"))) {
+ List<Element> testCaseElements =
List.copyOf(UtilXml.childElementList(mainElement, UtilMisc.toSet("test-case",
"test-group")));
+ for (Element testCaseElement :
selectTestCaseElements(testCaseElements, testCase)) {
String caseName = testCaseElement.getAttribute("case-name");
String nodeName = testCaseElement.getNodeName();
- if (testCase == null || caseName.equals(testCase)) {
- if ("test-case".equals(nodeName)) {
- parseTestElement(caseName,
UtilXml.firstChildElement(testCaseElement));
- } else if ("test-group".equals(nodeName)) {
- int i = 0;
- for (Element childElement:
UtilXml.childElementList(testCaseElement)) {
- parseTestElement(caseName + '-' + i, childElement);
- i++;
- }
+ if ("test-case".equals(nodeName)) {
+ parseTestElement(caseName,
UtilXml.firstChildElement(testCaseElement));
+ } else if ("test-group".equals(nodeName)) {
+ int i = 0;
+ for (Element childElement:
UtilXml.childElementList(testCaseElement)) {
+ parseTestElement(caseName + '-' + i, childElement);
+ i++;
}
}
}
}
+ /**
+ * Selects which of a suite's ordered test-case/test-group elements should
run for a given
+ * {@code case=} filter.
+ *
+ * <p>A single case is rarely self-contained: every testdef file in this
repo follows the
+ * convention of a data-load case (an {@code entity-xml action="load"},
typically named
+ * {@code *-data-load}/{@code load*TestData}) declared immediately before
the case(s) that consume
+ * it - see e.g. applications/party/testdef/PartyTests.xml's {@code
party-tests-data-load} feeding
+ * {@code party-tests}. Filtering down to only the exact requested
case-name, the way this method
+ * used to work, silently drops that prerequisite and leaves the requested
case failing for lack of
+ * data it never asked to be responsible for loading itself.
+ *
+ * <p>Only that one shape - an {@code entity-xml}/{@code
entity-xml-assert} case whose
+ * {@code action} is {@code "load"} - is auto-included as a prerequisite;
every other preceding
+ * case is skipped unless it is itself the requested case. That is
deliberately narrower than
+ * "run everything declared before the requested case": a {@code load} is
additive and
+ * side-effect-free relative to every other case in the suite, so running
it unconditionally is
+ * safe, but nothing else in a testdef file has that property -
+ * <ul>
+ * <li>A {@code jupiter-test-suite}/{@code junit-test-suite} case, or a
{@code test-group} (a
+ * repo-wide scan found 14 of 15 {@code test-group} children are themselves
+ * {@code jupiter-test-suite}), is a whole independent test class in its
own right. Several
+ * testdef files (e.g. framework/entity/testdef/entitytests.xml) bundle
multiple such classes into
+ * one {@code <test-suite>} purely for organization, with no data
relationship between them -
+ * requesting entity-crypto-tests (declared third) used to also run the
unrelated, much larger and
+ * deliberately slow entity-tests/entity-util-tests classes ahead of it,
turning a single-case
+ * request into most of the suite's runtime.</li>
+ * <li>A {@code service-test} is always a real functional test in every
testdef file in this repo,
+ * never data loading (see framework/service/testdef/servicetests.xml's
+ * service-dead-lock-retry-test and friends).</li>
+ * <li>An {@code entity-xml} whose action is {@code "assert"} (the default
when unset) is a check,
+ * not a load - in servicetests.xml,
service-eca-global-event-exec-assert-data verifies the side
+ * effects of the service-test case declared immediately before it.
Auto-including an assert case
+ * without the real test whose effects it checks would assert against data
that was never
+ * created - trading one bug (a missing prerequisite) for another (a
spurious failure) rather than
+ * fixing it.</li>
+ * </ul>
+ *
+ * <p>Nothing declared after the requested case runs, so this is not
simply "ignore the filter":
+ * a case still can't see data set up by a case that only runs after it in
the suite's own
+ * declared order.
+ *
+ * <p>When {@code testCase} is {@code null} (no filter - run the whole
suite), every element is
+ * selected unchanged. When {@code testCase} names a case-name that isn't
present in this
+ * particular document at all, nothing is selected - unchanged from the
old exact-match
+ * behavior, and still what lets JunitSuiteWrapper's suite-name filtering
keep a testdef file
+ * that simply doesn't define the requested case from contributing
anything.
+ * @param testCaseElements the suite's test-case/test-group elements, in
declared order
+ * @param testCase the requested case-name, or {@code null} to select
every element
+ * @return the elements to run, in the same order
+ */
+ static List<Element> selectTestCaseElements(List<Element>
testCaseElements, String testCase) {
+ if (testCase == null) {
+ return testCaseElements;
+ }
+ List<Element> selected = new ArrayList<>();
+ for (Element element : testCaseElements) {
+ boolean isTarget =
testCase.equals(element.getAttribute("case-name"));
+ if (isTarget) {
+ selected.add(element);
+ return selected;
+ }
+ if (isDataLoadCase(element)) {
+ selected.add(element);
+ }
+ }
+ return List.of();
+ }
+
+ /**
+ * True for a {@code <test-case>} whose sole child is an {@code
entity-xml}/{@code entity-xml-assert}
+ * element with {@code action="load"}: the one shape safe to auto-include
as a prerequisite ahead
+ * of a requested case. See selectTestCaseElements()'s javadoc for why
every other shape - including
+ * the same elements with a different action - is excluded instead.
+ * @param testCaseElement one of a suite's test-case/test-group elements
+ * @return true if this element is a genuine data-load case
+ */
+ private static boolean isDataLoadCase(Element testCaseElement) {
+ if (!"test-case".equals(testCaseElement.getNodeName())) {
+ return false;
+ }
+ Element child = UtilXml.firstChildElement(testCaseElement);
+ if (child == null) {
+ return false;
+ }
+ String nodeName = child.getNodeName();
+ boolean isEntityXml = "entity-xml".equals(nodeName) ||
"entity-xml-assert".equals(nodeName);
+ return isEntityXml && "load".equals(child.getAttribute("action"));
+ }
+
private void parseTestElement(String caseName, Element testElement) {
String nodeName = testElement.getNodeName();
if ("junit-test-suite".equals(nodeName)) {
diff --git
a/framework/testtools/src/test/java/org/apache/ofbiz/testtools/ModelTestSuiteTest.java
b/framework/testtools/src/test/java/org/apache/ofbiz/testtools/ModelTestSuiteTest.java
new file mode 100644
index 0000000000..026d8921ef
--- /dev/null
+++
b/framework/testtools/src/test/java/org/apache/ofbiz/testtools/ModelTestSuiteTest.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+
*******************************************************************************/
+package org.apache.ofbiz.testtools;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ofbiz.base.util.UtilXml;
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Exercises ModelTestSuite.selectTestCaseElements() directly: the piece of
the case-filtering logic
+ * that decides which of a suite's test-case/test-group elements run for a
given {@code case=} filter,
+ * with no Delegator/LocalDispatcher or class-loading involved - unlike the
constructor itself (see
+ * TestRunContainerTest's class javadoc for why that needs a full ofbiz --test
container bootstrap).
+ *
+ * <p>The fixture mirrors every shape actually found in this repo's testdef
files (a full repo-wide
+ * scan backs each case here):
+ * <ul>
+ * <li>{@code data-load} - an {@code entity-xml action="load"} case, the one
shape that genuinely
+ * behaves like setup: additive, side-effect-free relative to every other
case, safe to run
+ * unconditionally. This is the only shape auto-included as a
prerequisite.</li>
+ * <li>{@code assert-data} - an {@code entity-xml action="assert"} case.
Despite also being
+ * {@code entity-xml}, this is a check, not a load - and in the real suite it
is modeled on
+ * (framework/service/testdef/servicetests.xml's *-assert-data cases), it
verifies the side effects of
+ * the {@code service-test} case declared immediately before it.
Auto-including it without that
+ * service-test would assert against data that was never created, turning a
clean single-case run into
+ * a spurious failure - so it is treated the same as an independent test, not
as setup.</li>
+ * <li>{@code service-call} - a {@code service-test} case, modeled on
servicetests.xml's
+ *
service-dead-lock-retry-test/service-own-tx-sub-service-after-set-rollback-only-in-parent/
+ * service-eca-global-event-exec: always a real functional test in every
testdef file in this repo,
+ * never data loading.</li>
+ * <li>{@code first-tests}/{@code second-tests} - independent {@code
jupiter-test-suite} classes, the
+ * entitytests.xml shape this whole rule exists for (see
caseFilterSkipsAnIndependentJupiterTestClass
+ * DeclaredBeforeIt()'s comment).</li>
+ * <li>{@code grouped-tests} - a {@code test-group}. A repo-wide scan of every
{@code test-group} in
+ * this codebase found 14 of 15 child elements are {@code jupiter-test-suite}
(accounting/product/scrum
+ * testdef files bundle several independent classes into one group) and only 1
is {@code entity-xml} -
+ * so a test-group reads as "more independent classes," not setup, and is
excluded like one.</li>
+ * <li>{@code third-tests} - a {@code junit-test-suite} case, the
legacy-JUnit-3 equivalent of
+ * first-tests/second-tests.</li>
+ * </ul>
+ */
+class ModelTestSuiteTest {
+
+ private static final String SUITE_XML =
+ "<test-suite suite-name=\"sample\">"
+ + "<test-case case-name=\"data-load\"><entity-xml
action=\"load\" entity-xml-url=\"x\"/></test-case>"
+ + "<test-case case-name=\"assert-data\"><entity-xml
action=\"assert\" entity-xml-url=\"x\"/></test-case>"
+ + "<test-case case-name=\"service-call\"><service-test
service-name=\"x\"/></test-case>"
+ + "<test-case
case-name=\"first-tests\"><jupiter-test-suite
class-name=\"a.First\"/></test-case>"
+ + "<test-case
case-name=\"second-tests\"><jupiter-test-suite
class-name=\"a.Second\"/></test-case>"
+ + "<test-group case-name=\"grouped-tests\">"
+ + "<jupiter-test-suite class-name=\"a.GroupA\"/>"
+ + "<jupiter-test-suite class-name=\"a.GroupB\"/>"
+ + "</test-group>"
+ + "<test-case case-name=\"third-tests\"><junit-test-suite
class-name=\"a.Third\"/></test-case>"
+ + "</test-suite>";
+
+ @Test
+ void nullCaseFilterSelectsEveryElementUnchanged() throws Exception {
+ List<Element> selected =
ModelTestSuite.selectTestCaseElements(caseElements(), null);
+
+ assertThat(caseNamesOf(selected), contains(
+ "data-load", "assert-data", "service-call", "first-tests",
"second-tests", "grouped-tests", "third-tests"));
+ }
+
+ @Test
+ void caseFilterIncludesAGenuineLoadCaseDeclaredBeforeIt() throws Exception
{
+ List<Element> selected =
ModelTestSuite.selectTestCaseElements(caseElements(), "assert-data");
+
+ assertThat(caseNamesOf(selected), contains("data-load",
"assert-data"));
+ }
+
+ @Test
+ void caseFilterExcludesAnEntityXmlAssertCaseThatIsNotTheTarget() throws
Exception {
+ // assert-data would run against data only service-call creates -
since service-call is
+ // itself excluded (it's a real test, not setup), running assert-data
too would be a spurious
+ // failure, not a harmless extra. Modeled on servicetests.xml's
service-eca-global-event-exec
+ // -assert-data, which checks service-eca-global-event-exec's side
effects.
+ List<Element> selected =
ModelTestSuite.selectTestCaseElements(caseElements(), "first-tests");
+
+ assertThat(caseNamesOf(selected), contains("data-load",
"first-tests"));
+ }
+
+ @Test
+ void caseFilterSkipsAnIndependentJupiterTestClassDeclaredBeforeIt() throws
Exception {
+ // Regression test: entitytests.xml bundles five independent
jupiter-test-suite classes with
+ // no data-load relationship between them. Requesting
entity-crypto-tests (declared third)
+ // must not also run the unrelated entity-tests/entity-util-tests
classes ahead of it - that
+ // silently multiplied a single-case run into the whole suite's worth
of work, including
+ // EntityTestSuite's deliberately slow bulk-operation tests.
+ List<Element> selected =
ModelTestSuite.selectTestCaseElements(caseElements(), "second-tests");
+
+ assertThat(caseNamesOf(selected), contains("data-load",
"second-tests"));
+ }
+
+ @Test
+ void caseFilterExcludesEveryNonLoadShapeAheadOfTheTarget() throws
Exception {
+ // third-tests is declared last: only the one genuine entity-xml
action="load" case ahead of
+ // it survives. assert-data, service-call, first-tests, second-tests,
and grouped-tests are
+ // all excluded - none of them is data loading, whatever shape they
otherwise take.
+ List<Element> selected =
ModelTestSuite.selectTestCaseElements(caseElements(), "third-tests");
+
+ assertThat(caseNamesOf(selected), contains("data-load",
"third-tests"));
+ }
+
+ @Test
+ void caseFilterOnATestGroupSelectsOnlyThatGroup() throws Exception {
+ List<Element> selected =
ModelTestSuite.selectTestCaseElements(caseElements(), "grouped-tests");
+
+ assertThat(caseNamesOf(selected), contains("data-load",
"grouped-tests"));
+ }
+
+ @Test
+ void caseFilterOnTheFirstElementSelectsOnlyThatElement() throws Exception {
+ List<Element> selected =
ModelTestSuite.selectTestCaseElements(caseElements(), "data-load");
+
+ assertThat(caseNamesOf(selected), contains("data-load"));
+ }
+
+ @Test
+ void caseFilterNotPresentInThisDocumentSelectsNothing() throws Exception {
+ List<Element> selected =
ModelTestSuite.selectTestCaseElements(caseElements(), "not-here-at-all");
+
+ assertThat(selected, is(empty()));
+ }
+
+ private static List<Element> caseElements() throws Exception {
+ Document document = UtilXml.readXmlDocument(SUITE_XML, false);
+ return
List.copyOf(UtilXml.childElementList(document.getDocumentElement(),
Set.of("test-case", "test-group")));
+ }
+
+ private static List<String> caseNamesOf(List<Element> elements) {
+ return elements.stream().map(element ->
element.getAttribute("case-name")).toList();
+ }
+}
diff --git
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/event/JavaEventHandler.java
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/event/JavaEventHandler.java
index a101770d76..e2da1dcde3 100644
---
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/event/JavaEventHandler.java
+++
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/event/JavaEventHandler.java
@@ -76,10 +76,24 @@ public class JavaEventHandler implements EventHandler {
if (Debug.verboseOn()) {
Debug.logVerbose("[Processing]: Java Event", MODULE);
}
+ // A type="java" event defaults to running inside one transaction
spanning the whole
+ // invoke() call - the same global-transaction="false" opt-out
ServiceMultiEventHandler
+ // already honors for type="service-multi" events. Without it, an
event whose own method
+ // deliberately manages several independent units of work (e.g.
RunTestEvents running a
+ // whole testdef suite's worth of otherwise-independent test-cases
through TestRunContainer)
+ // has every one of those units silently folded into this one ambient
transaction instead:
+ // an entity-xml load's own begin()/commit() inside that event becomes
a no-op participant
+ // rather than a real commit, so a require-new-transaction service
called by a later,
+ // unrelated test-case in the same suite can't see data an earlier one
just "committed",
+ // and one test-case marking the transaction rollback-only poisons
every test-case after it
+ // in the same event invocation - see the 2026-08-15
ecommerce/entity/service RunTest
+ // investigation this comment was added for.
boolean began = false;
try {
- int timeout = Integer.max(event.getTransactionTimeout(), 0);
- began = TransactionUtil.begin(timeout);
+ if (event.isGlobalTransaction()) {
+ int timeout = Integer.max(event.getTransactionTimeout(), 0);
+ began = TransactionUtil.begin(timeout);
+ }
Method m = k.getMethod(event.getInvoke(), HttpServletRequest.class,
HttpServletResponse.class);
String ret = (String) m.invoke(null, request, response);
diff --git a/framework/webtools/webapp/webtools/WEB-INF/controller.xml
b/framework/webtools/webapp/webtools/WEB-INF/controller.xml
index dcdf7ab396..c579a9206a 100644
--- a/framework/webtools/webapp/webtools/WEB-INF/controller.xml
+++ b/framework/webtools/webapp/webtools/WEB-INF/controller.xml
@@ -445,13 +445,27 @@ under the License.
<request-map uri="RunTest/{compName}/{suiteName}">
<security https="true" auth="true"/>
- <event type="java"
path="org.apache.ofbiz.webtools.artifactinfo.RunTestEvents" invoke="runTest"/>
+ <!-- TestRunContainer runs a whole testdef suite's worth of
otherwise-independent
+ test-cases; each manages its own transaction the same way it does
when run via
+ the ofbiz command line's own test option, which never runs inside
an event's
+ ambient transaction to begin with. Without
global-transaction="false" here,
+ JavaEventHandler's default whole-invoke() transaction folds every
one of those
+ test-cases into a single ambient transaction instead, breaking
both data
+ visibility between test-cases and per-suite rollback-only
isolation. -->
+ <event type="java"
path="org.apache.ofbiz.webtools.artifactinfo.RunTestEvents" invoke="runTest"
global-transaction="false"/>
<response name="success" type="request" value="TestSuiteInfo"/>
<response name="error" type="request" value="TestSuiteInfo"/>
</request-map>
<request-map uri="RunTest/{compName}/{suiteName}/{caseName}">
<security https="true" auth="true"/>
- <event type="java"
path="org.apache.ofbiz.webtools.artifactinfo.RunTestEvents" invoke="runTest"/>
+ <!-- TestRunContainer runs a whole testdef suite's worth of
otherwise-independent
+ test-cases; each manages its own transaction the same way it does
when run via
+ the ofbiz command line's own test option, which never runs inside
an event's
+ ambient transaction to begin with. Without
global-transaction="false" here,
+ JavaEventHandler's default whole-invoke() transaction folds every
one of those
+ test-cases into a single ambient transaction instead, breaking
both data
+ visibility between test-cases and per-suite rollback-only
isolation. -->
+ <event type="java"
path="org.apache.ofbiz.webtools.artifactinfo.RunTestEvents" invoke="runTest"
global-transaction="false"/>
<response name="success" type="request" value="TestSuiteInfo"/>
<response name="error" type="request" value="TestSuiteInfo"/>
</request-map>