From 33de73b3e82ab0c2ad1216b391e841b714ac3149 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Wed, 5 Aug 2026 21:35:15 -0400 Subject: [PATCH 01/11] perf(test): move ContentTypeResourceTests to template postman group (#36914) Category Content ran ~40m with ContentTypeResourceTests alone taking 14m37s of it, while the Template job finished in ~10m (mostly boot overhead). Rebalancing the collection across existing groups cuts the Postman critical path ~15m without adding a job or paying another ~12m dotCMS boot. Co-Authored-By: Claude Fable 5 --- dotcms-postman/config.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dotcms-postman/config.json b/dotcms-postman/config.json index dca5b14e7a41..024a7bc6beb7 100644 --- a/dotcms-postman/config.json +++ b/dotcms-postman/config.json @@ -8,7 +8,6 @@ "collections": [ "Category.postman_collection", "ContentResourceV1.postman_collection", - "ContentTypeResourceTests", "Content_Resource.postman_collection" ] }, @@ -45,7 +44,10 @@ }, { "name": "template", - "collections": ["Template_Resource.postman_collection"] + "collections": [ + "Template_Resource.postman_collection", + "ContentTypeResourceTests" + ] }, { "name": "workflow", From 88d993b6ea41597dd3cfe00083632fe9e5d0bbe2 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Wed, 5 Aug 2026 22:34:05 -0400 Subject: [PATCH 02/11] fix(test): make ContentTypeResourceTests ensure-param tests self-contained (#36914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ensure' tests assert a Video content type exists, but nothing creates it eagerly at startup — it only existed because collections that previously ran before this one in the category-content group created it indirectly. Moving the collection to the template group exposed this (Video missing, dotAsset present). Add a create-if-missing setup request so the collection passes regardless of group placement or ordering. Co-Authored-By: Claude Fable 5 --- .../postman/ContentTypeResourceTests.json | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json b/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json index fd9694047dfd..ec84616782de 100644 --- a/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json +++ b/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json @@ -1866,6 +1866,66 @@ { "name": "Test Get ContentTypes", "item": [ + { + "name": "Ensure Video DotAsset type exists (setup)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Setup: the 'ensure' tests in this folder expect a 'Video' content type to exist.", + "// Nothing in dotCMS creates it eagerly at startup, so when this collection runs", + "// without the collections that create it indirectly, the assertions fail.", + "// Create it if missing so this collection is self-contained (400 = already exists).", + "pm.test(\"Video type exists or was created\", function () {", + " pm.expect([200, 201, 400]).to.include(pm.response.code);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"clazz\": \"com.dotcms.contenttype.model.type.ImmutableDotAssetContentType\",\n \"defaultType\": false,\n \"fixed\": false,\n \"system\": false,\n \"folder\": \"SYSTEM_FOLDER\",\n \"name\": \"Video\",\n \"variable\": \"Video\",\n \"workflow\": [\n \"d61a59e1-a49c-46f2-a929-db2b4bfa88b2\"\n ]\n}" + }, + "url": { + "raw": "{{serverURL}}/api/v1/contenttype", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "contenttype" + ] + } + }, + "response": [] + }, { "name": "Get ContentTypes sending HostID", "event": [ From fd299859ea282969209268cf4a337fc598ad38f7 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Thu, 6 Aug 2026 17:56:30 -0400 Subject: [PATCH 03/11] Update ContentTypeResourceTests.json structure --- .../src/main/resources/postman/ContentTypeResourceTests.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json b/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json index ec84616782de..355a57d8ffa0 100644 --- a/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json +++ b/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json @@ -4,6 +4,7 @@ "name": "ContentType Resource", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "5403727" + }, "item": [ { @@ -15952,4 +15953,4 @@ } } ] -} \ No newline at end of file +} From ff8a39df2ddec77c0fc8c434a4dfe4bdcb5cddaf Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 7 Aug 2026 10:01:32 -0400 Subject: [PATCH 04/11] perf(ci): rebalance IT and Postman shards on measured time, drop dead artifact The PR pipeline's wall clock is bounded by its slowest test job. Two jobs were tied at the top -- Integration MainSuite 1a (38.9m) and Postman GraphQL (37.8m) -- so rebalancing either one alone moved nothing. This rebalances both, plus removes an artifact that was uploaded on every build and consumed by nothing. Measured from GitHub Actions job timings and failsafe/newman reports: Integration 7 shards, 130.8m of test time, max 38.9m, ideal 26.5m Postman 11 shards, 147.8m of test time, max 37.8m, ideal 22.8m fixed cost per shard: ~7.6m (IT), ~9.4m (Postman) Changes: * Integration: repack all 557 classes across 7 MainSuite shards by measured per-class time (LPT bin-packing) instead of by accretion. Every shard is now 18.4m of test time (spread 0.0m), vs 14.2m-31.1m before. Adds MainSuite3b and MainSuite4a. Class count is a poor proxy for time and was the reason the old split drifted -- one shard held 298 classes/31m, another 52 classes/14m. Suites now use fully-qualified class names so rebalancing does not churn imports. The "run FIRST on purpose" full-scan block from #36911 is preserved at the head of each shard. * Postman: regroup on measured newman time, 11 shards -> 9, all at 16.4m. GraphQLTests was a single 25.6m collection and the binding constraint on the whole Postman tail -- no regrouping could get below 25.6m + 9.4m overhead. It is now folder-sharded via a new `folders` key in config.json rather than by splitting the 518KB collection file. Four micro-groups (ai, pp, container, template) that were ~85% fixed overhead are merged away. * index.js: support the `folders` key, and hard-fail on a folder name that does not exist. Newman runs zero requests for an unknown folder and still exits green, so a typo would silently delete coverage. `errors` never affected the exit code -- only `failures` did -- so this validates up front and exits 1, consistent with how an unknown groupname is already handled. * verify-config.js: checks config.json against the collections on disk -- missing collections, unknown folders, double-claimed collections, and folders covered by no shard. It caught a real bug while writing this: a folder named "Related content with condition / query" contains a slash, and deriving names by splitting report labels on " / " silently truncated it. * maven-job: drop the `build-classes` artifact. It was uploaded on every build (~0.5m on the serial critical path, since the build gates every test job) and downloaded by nobody -- the only `restore-classes: true` caller is deploy-javadoc, which no workflow invokes. Net job count is unchanged: integration 7 -> 9 shards, postman 11 -> 9. Expected tail: 38.9m -> ~26m. Wall clock above that floor is queue time, which this does not address. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NH4Pes5S9d1AQorJBNeFJU --- .../deployment/deploy-javadoc/action.yml | 1 - .github/actions/core-cicd/maven-job/README.md | 1 - .../actions/core-cicd/maven-job/action.yml | 25 - .github/test-matrix.yml | 58 +- .../src/test/java/com/dotcms/MainSuite1a.java | 193 +++--- .../src/test/java/com/dotcms/MainSuite1b.java | 180 ++--- .../src/test/java/com/dotcms/MainSuite2a.java | 197 +++--- .../src/test/java/com/dotcms/MainSuite2b.java | 637 +++--------------- .../src/test/java/com/dotcms/MainSuite3a.java | 191 +++--- .../src/test/java/com/dotcms/MainSuite3b.java | 108 +++ .../src/test/java/com/dotcms/MainSuite4a.java | 108 +++ dotcms-postman/config.json | 176 +++-- dotcms-postman/index.js | 40 +- dotcms-postman/verify-config.js | 98 +++ 14 files changed, 962 insertions(+), 1051 deletions(-) create mode 100644 dotcms-integration/src/test/java/com/dotcms/MainSuite3b.java create mode 100644 dotcms-integration/src/test/java/com/dotcms/MainSuite4a.java create mode 100644 dotcms-postman/verify-config.js diff --git a/.github/actions/core-cicd/deployment/deploy-javadoc/action.yml b/.github/actions/core-cicd/deployment/deploy-javadoc/action.yml index a544a034dee2..137ad046b616 100644 --- a/.github/actions/core-cicd/deployment/deploy-javadoc/action.yml +++ b/.github/actions/core-cicd/deployment/deploy-javadoc/action.yml @@ -49,7 +49,6 @@ runs: maven-args: "javadoc:javadoc -pl :dotcms-core" generate-docker: false cleanup-runner: true - restore-classes: true artifacts-from: ${{ inputs.artifact-run-id }} github-token: ${{ inputs.github-token }} diff --git a/.github/actions/core-cicd/maven-job/README.md b/.github/actions/core-cicd/maven-job/README.md index b9e1284d3371..a44120906726 100644 --- a/.github/actions/core-cicd/maven-job/README.md +++ b/.github/actions/core-cicd/maven-job/README.md @@ -27,7 +27,6 @@ This GitHub Action sets up and runs a Maven job with extensive configuration opt | `dotcms-license` | The license key for dotCMS | No | `''` | | `artifacts-from` | Download artifacts from a previous job | No | `''` | | `github-token` | GitHub token for authentication | Yes | - | -| `restore-classes` | Restore build classes | No | `false` | | `stage-name` | Stage name for the build | Yes | - | | `maven-args` | Arguments for Maven build | Yes | - | | `generates-test-results` | Generate test results artifacts | No | `false` | diff --git a/.github/actions/core-cicd/maven-job/action.yml b/.github/actions/core-cicd/maven-job/action.yml index 1a8cbf80bd5c..e06b733eca56 100644 --- a/.github/actions/core-cicd/maven-job/action.yml +++ b/.github/actions/core-cicd/maven-job/action.yml @@ -61,10 +61,6 @@ inputs: github-token: description: 'GitHub token for authentication' required: true - restore-classes: - description: 'Restore build classes' - required: false - default: 'false' stage-name: description: 'Stage name for the build' required: true @@ -262,15 +258,6 @@ runs: if: ${{ inputs.needs-docker-image == 'true' }} run: docker load < /tmp/docker-image/image.tar - - id: restore-artifact-classes - name: Restore Classes - if: ${{ inputs.restore-classes == 'true' }} - uses: actions/download-artifact@v4 - with: - run-id: ${{ inputs.artifacts-from }} - github-token: ${{ inputs.github-token }} - name: build-classes${{ steps.artifact-suffix.outputs.suffix }} - - name: Docker Hub Login if: ${{ inputs.docker-io-username != '' && inputs.docker-io-token != '' }} uses: docker/login-action@v3.0.0 @@ -383,18 +370,6 @@ runs: name: docker-image${{ steps.artifact-suffix.outputs.suffix }} path: /tmp/image.tar - - id: persist-build-classes - name: Persist Build Classes - if: ${{ inputs.generate-artifacts == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: build-classes${{ steps.artifact-suffix.outputs.suffix }} - path: | - **/target/classes/**/*.class - **/target/generated-sources/**/*.java - **/target/test-classes/**/*.class - LICENSE - - id: delete-built-artifacts-from-cache name: Delete Built Artifacts From Cache if: ${{ inputs.generate-artifacts == 'true' && steps.restore-cache-maven.outputs.cache-hit != 'true' }} diff --git a/.github/test-matrix.yml b/.github/test-matrix.yml index d9fe80aa8168..e6fc357314a8 100644 --- a/.github/test-matrix.yml +++ b/.github/test-matrix.yml @@ -62,6 +62,10 @@ test_types: verify -Dit.test.forkcount=1 -pl :dotcms-integration -Dcoreit.test.skip=false suites: + # Shards are balanced on measured per-class test time (~18.4m each), not on + # class count - class count is a poor proxy (one shard was 298 classes/31m, + # another 52 classes/14m). Rebalance with the same measurement when shard + # times drift apart; see scripts referenced in the PR that introduced this. - name: "Integration Tests - MainSuite 1a" test_class: "MainSuite1a" stage_name: "IT Tests MainSuite 1a" @@ -77,6 +81,12 @@ test_types: - name: "Integration Tests - MainSuite 3a" test_class: "MainSuite3a" stage_name: "IT Tests MainSuite 3a" + - name: "Integration Tests - MainSuite 3b" + test_class: "MainSuite3b" + stage_name: "IT Tests MainSuite 3b" + - name: "Integration Tests - MainSuite 4a" + test_class: "MainSuite4a" + stage_name: "IT Tests MainSuite 4a" - name: "Integration Tests - Junit5 Suite 1" test_class: "Junit5Suite1" stage_name: "IT Tests Junit5Suite1" @@ -101,36 +111,34 @@ test_types: base_maven_args: >- verify -pl :dotcms-postman -Dpostman.test.skip=false + # Groups are balanced on measured newman time (~16.4m each) and defined in + # dotcms-postman/config.json. Every shard pays ~9.4m of fixed cost (artifact + # download, docker load, dotCMS boot), so a handful of tiny groups is pure + # waste - keep groups few and evenly sized rather than thematically pure. + # + # `default` is special: index.js computes it as every collection on disk NOT + # listed in config.json, so it is the safety net that runs newly added + # collections. It must always have a shard here. suites: - # AI and ML related tests - - name: "Postman Tests - AI" - collection: "ai" - - # Content management tests - - name: "Postman Tests - Category Content" - collection: "category-content" - - name: "Postman Tests - Container" - collection: "container" - - name: "Postman Tests - Page" - collection: "page" + - name: "Postman Tests - Content" + collection: "content" + - name: "Postman Tests - ContentType" + collection: "contenttype" + # GraphQLTests is one 25.6m collection - too big for a single shard and the + # binding constraint on the whole Postman tail. config.json splits it by + # top-level folder instead of splitting the 518KB collection file. + - name: "Postman Tests - GraphQL A" + collection: "graphql-a" + - name: "Postman Tests - GraphQL B" + collection: "graphql-b" + - name: "Postman Tests - Pages" + collection: "pages" + - name: "Postman Tests - Site" + collection: "site" - name: "Postman Tests - Template" collection: "template" - - # Feature-specific tests - - name: "Postman Tests - Experiment" - collection: "experiment" - - name: "Postman Tests - GraphQL" - collection: "graphql" - name: "Postman Tests - Workflow" collection: "workflow" - - # Push/Publish tests - - name: "Postman Tests - PP" - collection: "pp" - - # Default test suites - - name: "Postman Tests - Default Split" - collection: "default-split" - name: "Postman Tests - Default" collection: "default" diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite1a.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite1a.java index 2d7da2cfafc5..04d266e1c577 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite1a.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite1a.java @@ -1,45 +1,19 @@ package com.dotcms; -import com.dotcms.ai.workflow.OpenAIGenerateImageActionletTest; -import com.dotcms.analytics.track.RequestMatcherTest; -import com.dotcms.contenttype.business.SiteAndFolderResolverImplTest; -import com.dotcms.enterprise.publishing.remote.PushPublishBundleGeneratorTest; -import com.dotcms.enterprise.publishing.remote.bundler.DependencyBundlerTest; -import com.dotcms.enterprise.publishing.remote.bundler.RuleBundlerTest; -import com.dotcms.enterprise.publishing.staticpublishing.StaticPublisherIntegrationTest; -import com.dotcms.enterprise.rules.RulesAPIImplIntegrationTest; -import com.dotcms.experiments.business.ExperimentAPIImpIntegrationTest; -import com.dotcms.experiments.business.ExperimentUrlPatternCalculatorIntegrationTest; -import com.dotcms.experiments.business.web.ExperimentWebAPIImplIntegrationTest; -import com.dotcms.graphql.DotGraphQLHttpServletTest; -import com.dotcms.integritycheckers.ContentFileAssetIntegrityCheckerTest; -import com.dotcms.integritycheckers.ContentPageIntegrityCheckerTest; -import com.dotcms.integritycheckers.FolderIntegrityCheckerTest; -import com.dotcms.integritycheckers.HostIntegrityCheckerTest; import com.dotcms.junit.MainBaseSuite; -import com.dotcms.publisher.bundle.business.BundleFactoryImplTest; -import com.dotcms.publisher.business.PublishQueueElementTransformerTest; -import com.dotcms.publisher.util.DependencyModDateUtilTest; -import com.dotcms.publishing.job.SiteSearchJobImplTest; -import com.dotcms.rendering.js.JsEngineTest; -import com.dotcms.rendering.velocity.viewtools.XsltToolTest; -import com.dotcms.storage.FileMetadataAPITest; -import com.dotcms.uuid.shorty.LegacyShortyIdApiTest; -import com.dotmarketing.cache.FolderCacheImplIntegrationTest; -import com.dotmarketing.portlets.contentlet.business.HostFactoryImplTest; -import com.dotmarketing.portlets.contentlet.business.web.ContentletWebAPIImplIntegrationTest; -import com.dotmarketing.portlets.workflows.actionlet.EmailActionletTest; -import com.dotmarketing.quartz.job.StartEndScheduledExperimentsJobTest; -import com.dotmarketing.startup.runonce.Task220825CreateVariantFieldTest; -import com.dotmarketing.startup.runonce.Task221007AddVariantIntoPrimaryKeyTest; -import com.dotmarketing.startup.runonce.Task240306MigrateLegacyLanguageVariablesTest; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; -/* grep -l -r "@Test" dotCMS/src/integration-test */ -/* ./gradlew integrationTest -Dtest.single=com.dotcms.MainSuite */ - - +/** + * Integration test suite shard 1 of 7. + * + * Shards are balanced on measured per-class test time so the slowest shard + * bounds the CI critical path as tightly as possible. When adding a test, + * put it in the shard with the lowest total time rather than appending here + * by habit - see .github/test-matrix.yml for the shard list. + * + * Classes are fully qualified so that rebalancing does not churn imports. + */ @RunWith(MainBaseSuite.class) @SuiteClasses({ @@ -48,85 +22,86 @@ // that walks the whole dataset (executeUpgrade, findAll*) costs // O(all content created so far). Scheduled late these pay for every // preceding test's leftovers. Keep new full-scan tests in this block. - Task240306MigrateLegacyLanguageVariablesTest.class, - com.dotmarketing.portlets.templates.business.TemplateAPITest.class, - com.dotmarketing.portlets.containers.business.ContainerAPIImplTest.class, + com.dotmarketing.startup.runonce.Task240306MigrateLegacyLanguageVariablesTest.class, + com.dotmarketing.factories.MultiTreeAPITest.class, - StartEndScheduledExperimentsJobTest.class, - RulesAPIImplIntegrationTest.class, - ExperimentAPIImpIntegrationTest.class, - ExperimentWebAPIImplIntegrationTest.class, - ContentletWebAPIImplIntegrationTest.class, // moved to top because of failures on GHA - DependencyBundlerTest.class, // moved to top because of failures on GHA - SiteAndFolderResolverImplTest.class, //Moved up to avoid conflicts with CT deletion - FolderCacheImplIntegrationTest.class, - StaticPublisherIntegrationTest.class, - com.dotcms.publishing.PublisherAPIImplTest.class, - SiteSearchJobImplTest.class, - XsltToolTest.class, - PushPublishBundleGeneratorTest.class, - LegacyShortyIdApiTest.class, - RuleBundlerTest.class, - org.apache.velocity.runtime.parser.node.SimpleNodeTest.class, - com.liferay.portal.ejb.UserLocalManagerTest.class, + com.dotcms.vanityurl.business.VanityUrlAPITest.class, + com.dotcms.enterprise.publishing.remote.bundler.DependencyBundlerTest.class, + com.dotmarketing.filters.FiltersTest.class, + com.dotcms.experiments.business.web.ExperimentWebAPIImplIntegrationTest.class, + com.dotmarketing.business.PermissionAPITest.class, + com.dotcms.rest.api.v1.page.PageRenderSourcesResourceTest.class, + com.dotcms.rest.api.v1.folder.FolderResourceTest.class, + com.dotcms.enterprise.publishing.staticpublishing.StaticPublisherIntegrationTest.class, + com.dotcms.security.apps.AppsAPIImplTest.class, + com.dotcms.rendering.velocity.viewtools.content.ContentMapTest.class, + com.dotcms.contenttype.business.FieldAPITest.class, + com.dotcms.graphql.business.GraphqlAPITest.class, + com.dotmarketing.util.contentlet.pagination.PaginatedContentletsIntegrationTest.class, + com.dotcms.rest.api.v1.publishing.PublishingResourceIntegrationTest.class, + com.dotmarketing.servlets.ShortyServletAndTitleImageTest.class, + com.dotcms.rendering.velocity.viewtools.ContainerWebAPIIntegrationTest.class, + com.dotcms.rendering.velocity.viewtools.FileToolTest.class, + com.dotmarketing.portlets.structure.factories.FieldFactoryTest.class, + com.dotcms.analytics.track.collectors.WebEventsCollectorServiceImplTest.class, + com.dotmarketing.portlets.workflows.actionlet.EmailActionletTest.class, + com.dotcms.rendering.velocity.viewtools.WorkflowToolTest.class, com.liferay.portal.ejb.UserUtilTest.class, - com.liferay.util.LocaleUtilTest.class, - com.dotcms.languagevariable.business.LanguageVariableAPITest.class, - com.dotcms.publishing.PublisherAPITest.class, - com.dotcms.publishing.remote.RemoteReceiverLanguageResolutionTest.class, - com.dotcms.cluster.business.ServerAPIImplTest.class, - com.dotcms.cache.KeyValueCacheImplTest.class, - com.dotcms.enterprise.publishing.remote.handler.RuleBundlerHandlerTest.class, - com.dotcms.enterprise.publishing.remote.CategoryBundlerHandlerTest.class, - com.dotcms.enterprise.publishing.remote.HostBundlerHandlerTest.class, - com.dotcms.enterprise.priv.ESSearchProxyTest.class, - com.dotcms.util.pagination.ContentTypesPaginatorTest.class, - com.dotcms.util.marshal.MarshalUtilsIntegrationTest.class, - com.dotcms.util.RelationshipUtilTest.class, - com.dotcms.util.ImportUtilTest.class, - com.dotcms.publisher.business.PublisherAPIImplTest.class, - PublishQueueElementTransformerTest.class, - com.dotmarketing.util.PageModeTest.class, - com.dotmarketing.business.web.UserWebAPIImplTest.class, - com.dotcms.auth.providers.jwt.JsonWebTokenUtilsIntegrationTest.class, - com.dotcms.auth.providers.jwt.factories.ApiTokenAPITest.class, - com.dotcms.auth.providers.jwt.services.JsonWebTokenServiceIntegrationTest.class, - DependencyModDateUtilTest.class, - com.dotcms.publisher.business.PublisherTest.class, - com.dotcms.enterprise.publishing.PublishDateUpdaterIntegrationTest.class, - com.dotcms.publisher.endpoint.bean.PublishingEndPointTest.class, - com.dotcms.publisher.endpoint.business.PublishingEndPointAPITest.class, - com.dotcms.publisher.endpoint.business.PublishingEndPointFactoryImplTest.class, + com.dotmarketing.quartz.job.StartEndScheduledExperimentsJobTest.class, + com.dotcms.publishing.PublisherAPIImplTest.class, + com.dotcms.keyvalue.busines.KeyValueAPIImplTest.class, + com.dotcms.rest.api.v1.drive.ContentDriveFieldFilterTest.class, + com.dotcms.telemetry.collectors.experiment.CountVariantsInAllDraftExperimentsMetricTypeTest.class, + com.dotcms.publisher.business.PublishAuditAPITest.class, + com.dotcms.util.pagination.ContainerPaginatorTest.class, + com.dotcms.ai.client.AIProxyClientTest.class, + com.dotcms.rendering.velocity.viewtools.navigation.NavToolCacheTest.class, + com.dotmarketing.portlets.contentlet.transform.BinaryToMapTransformerTest.class, + com.dotcms.ai.workflow.OpenAIGenerateImageActionletTest.class, + com.dotcms.timemachine.business.TimeMachineAPITest.class, + com.dotcms.storage.StoragePersistenceAPITest.class, + com.dotcms.rest.api.v2.contenttype.FieldResourceTest.class, + com.dotmarketing.db.HibernateUtilTest.class, + com.dotmarketing.quartz.job.EncryptPlainPasswordsJobTest.class, + com.dotmarketing.business.helper.PermissionHelperTest.class, + com.dotmarketing.startup.runonce.Task230426AlterVarcharLengthOfLockedByColTest.class, + com.dotmarketing.startup.runonce.Task201014UpdateColumnsValuesInIdentifierTableTest.class, + com.dotmarketing.startup.runonce.Task250826AddIndexesToUniqueFieldsTableTest.class, + com.dotmarketing.startup.runonce.Task210506UpdateStorageTableTest.class, + com.dotmarketing.business.RoleAPITest.class, com.dotcms.publisher.assets.business.PushedAssetsAPITest.class, - com.dotcms.notification.business.NotificationAPITest.class, - com.dotcms.business.LocalTransactionAndCloseDBIfOpenedFactoryTest.class, - com.dotcms.business.bytebuddy.ByteBuddyAdviceWeavingTest.class, - FolderIntegrityCheckerTest.class, - HostFactoryImplTest.class, - BundleFactoryImplTest.class, - ExperimentUrlPatternCalculatorIntegrationTest.class, - JsEngineTest.class, - EmailActionletTest.class, - OpenAIGenerateImageActionletTest.class, - RequestMatcherTest.class, - com.dotmarketing.portlets.rules.conditionlet.ConditionletOSGIFTest.class, + com.dotmarketing.business.CommitListenerCacheWrapperTest.class, + com.dotmarketing.portlets.workflows.actionlet.CopyActionletTest.class, + com.dotcms.enterprise.publishing.remote.CategoryBundlerHandlerTest.class, + com.dotcms.contenttype.business.DotAssetBaseTypeToContentTypeStrategyImplTest.class, + com.dotcms.util.TimeMachineUtilTest.class, com.dotmarketing.portlets.rules.conditionlet.CurrentSessionLanguageConditionletTest.class, - com.dotmarketing.portlets.rules.conditionlet.NumberOfTimesPreviouslyVisitedConditionletTest.class, - com.dotmarketing.portlets.rules.conditionlet.UsersBrowserLanguageConditionletTest.class, - com.dotmarketing.portlets.rules.conditionlet.UsersSiteVisitsConditionletTest.class, + com.dotmarketing.quartz.job.PruneTimeMachineBackupJobTest.class, + com.dotcms.saml.SamlConfigurationServiceTest.class, + com.dotcms.graphql.datafetcher.FolderCollectionDataFetcherTest.class, + com.dotmarketing.startup.runonce.Task241013RemoveFullPathLcColumnFromIdentifierTest.class, + com.dotmarketing.startup.runonce.Task210527DropReviewFieldsFromContentletTableTest.class, + com.dotcms.graphql.DotGraphQLHttpServletTest.class, + com.dotmarketing.util.ITConfigTest.class, + com.dotmarketing.startup.runonce.Task230630CreateRunningIdsExperimentFieldIntegrationTest.class, + com.dotcms.business.SystemTableFactoryTest.class, + com.dotmarketing.startup.runonce.Task210802UpdateStructureTableTest.class, + com.dotmarketing.business.web.LanguageWebApiTest.class, + com.dotcms.analytics.track.collectors.BasicProfileCollectorTest.class, + com.dotmarketing.startup.runonce.Task220824CreateDefaultVariantTest.class, + com.dotmarketing.portlets.folders.model.FolderTest.class, + com.dotmarketing.startup.runonce.Task260615AlterClusterIdLengthTest.class, + com.dotmarketing.startup.runonce.Task220214AddOwnerAndIDateToFolderTableTest.class, + com.dotmarketing.startup.runonce.Task230707CreateSystemTableTest.class, + com.dotcms.business.interceptor.InterceptorHandlerTest.class, + com.dotcms.content.business.ObjectMapperTest.class, + com.dotmarketing.startup.runonce.Task05370AddAppsPortletToLayoutTest.class, + com.dotmarketing.startup.runonce.Task210520UpdateAnonymousEmailTest.class, + com.dotcms.storage.Chainable404StorageCacheTest.class, com.dotmarketing.portlets.rules.conditionlet.VisitorOperatingSystemConditionletTest.class, - com.dotmarketing.portlets.rules.conditionlet.VisitedUrlConditionletTest.class, - com.dotmarketing.portlets.rules.business.RulesCacheFTest.class, - com.dotmarketing.portlets.folders.business.FolderAPITest.class, - com.dotmarketing.portlets.containers.business.ContainerAPITest.class, - com.dotmarketing.portlets.containers.business.FileAssetContainerUtilTest.class, - com.dotmarketing.portlets.htmlpages.business.HTMLPageAPITest.class, - com.dotmarketing.portlets.structure.factories.StructureFactoryTest.class, - com.dotmarketing.portlets.structure.factories.FieldFactoryTest.class, - com.dotmarketing.portlets.structure.model.ContentletRelationshipsTest.class, - com.dotmarketing.portlets.structure.transform.ContentletRelationshipsTransformerTest.class, + com.dotcms.security.apps.AppsCacheImplTest.class, + com.dotcms.api.web.HttpServletRequestImpersonatorTest.class }) - public class MainSuite1a { } diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite1b.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite1b.java index 0f5cc787de6a..720cc0c4eb32 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite1b.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite1b.java @@ -1,105 +1,107 @@ package com.dotcms; -import com.dotcms.graphql.DotGraphQLHttpServletTest; import com.dotcms.junit.MainBaseSuite; -import com.dotcms.storage.Chainable404StorageCacheTest; -import com.dotcms.storage.FileStorageAPITest; -import com.dotmarketing.common.db.DotConnectTest; -import com.dotmarketing.quartz.QuartzUtilsTest; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; -/* grep -l -r "@Test" dotCMS/src/integration-test */ -/* ./gradlew integrationTest -Dtest.single=com.dotcms.MainSuite */ - - +/** + * Integration test suite shard 2 of 7. + * + * Shards are balanced on measured per-class test time so the slowest shard + * bounds the CI critical path as tightly as possible. When adding a test, + * put it in the shard with the lowest total time rather than appending here + * by habit - see .github/test-matrix.yml for the shard list. + * + * Classes are fully qualified so that rebalancing does not churn imports. + */ @RunWith(MainBaseSuite.class) @SuiteClasses({ - com.dotcms.keyvalue.busines.KeyValueAPIImplTest.class, - com.dotcms.keyvalue.business.KeyValueAPITest.class, - com.dotcms.tika.TikaUtilsTest.class, - com.dotcms.visitor.filter.logger.VisitorLoggerTest.class, - com.dotcms.visitor.filter.characteristics.VisitorCharacterTest.class, - com.dotcms.graphql.business.GraphqlAPITest.class, - com.dotcms.contenttype.test.ContentTypeTest.class, - com.dotcms.contenttype.test.DeleteFieldJobTest.class, - com.dotcms.content.elasticsearch.business.ESSiteSearchAPITest.class, - com.dotcms.content.elasticsearch.business.ESMappingAPITest.class, - com.dotcms.content.elasticsearch.business.ContentletIndexAPIImplTest.class, - com.dotcms.contenttype.test.ContentTypeAPIImplTest.class, - com.dotcms.contenttype.test.ContentTypeBuilderTest.class, - com.dotcms.contenttype.test.ContentTypeFactoryImplTest.class, - com.dotcms.contenttype.test.ContentTypeImportExportTest.class, - com.dotcms.contenttype.test.FieldFactoryImplTest.class, - com.dotcms.contenttype.test.JsonContentTypeTransformerTest.class, - com.dotcms.contenttype.test.FieldBuilderTest.class, - com.dotcms.contenttype.test.KeyValueFieldUtilTest.class, - com.dotcms.contenttype.test.ContentTypeResourceTest.class, + + // Data-scanning tests run FIRST on purpose. + // Integration tests accumulate content and never clean up, so anything + // that walks the whole dataset (executeUpgrade, findAll*) costs + // O(all content created so far). Scheduled late these pay for every + // preceding test's leftovers. Keep new full-scan tests in this block. + com.dotmarketing.common.reindex.ReindexAPITest.class, + + com.dotcms.rendering.velocity.viewtools.content.util.ContentUtilsTest.class, + com.dotcms.content.elasticsearch.business.ESContentFactoryImplTest.class, + com.dotcms.enterprise.rules.RulesAPIImplIntegrationTest.class, + com.dotcms.publishing.job.SiteSearchJobImplTest.class, + com.dotcms.contenttype.business.uniquefields.extratable.UniqueFieldDataBaseUtilTest.class, + com.dotmarketing.portlets.contentlet.business.HostFactoryImplTest.class, + com.dotcms.graphql.datafetcher.page.ContentMapDataFetcherTest.class, + com.dotcms.publisher.util.DependencyManagerTest.class, + com.dotcms.rest.api.v1.asset.WebAssetHelperIntegrationTest.class, + com.dotcms.enterprise.publishing.remote.StaticPushPublishBundleGeneratorTest.class, + com.dotmarketing.portlets.contentlet.business.ContentletCheckInTest.class, com.dotcms.contenttype.business.RelationshipAPITest.class, - com.dotcms.contenttype.business.FieldAPITest.class, + com.dotcms.rest.api.v1.content.ContentResourceIntegrationTest.class, + com.dotcms.rest.api.v1.maintenance.MaintenanceResourceIntegrationTest.class, + com.dotcms.rest.elasticsearch.ESContentResourcePortletTest.class, + com.dotcms.experiments.business.RootIndexRegexUrlPatterStrategyIntegrationTest.class, + com.dotmarketing.portlets.contentlet.util.ContentletUtilTest.class, + com.dotcms.util.RelationshipUtilTest.class, + com.dotcms.auth.providers.jwt.factories.ApiTokenAPITest.class, + com.dotcms.enterprise.publishing.remote.HostBundlerHandlerTest.class, + com.dotmarketing.portlets.templates.business.TemplateFactoryImplTest.class, + com.dotmarketing.portlets.browser.ajax.BrowserAjaxTest.class, com.dotcms.contenttype.business.RelationshipFactoryImplTest.class, - com.dotcms.contenttype.model.field.layout.FieldLayoutColumnSerializerTest.class, - com.dotcms.contenttype.model.field.layout.FieldLayoutSerializerTest.class, - com.dotcms.contenttype.model.field.layout.FieldLayoutRowSerializerTest.class, - com.dotcms.contenttype.model.field.layout.FieldLayoutTest.class, - com.dotcms.workflow.helper.TestSystemActionMappingsHandlerMerger.class, - com.dotcms.concurrent.lock.DotKeyLockManagerTest.class, - com.dotcms.rendering.velocity.ASTMethodTest.class, + com.dotcms.rest.api.v1.container.ContainerResourceHostResolutionIT.class, + com.dotcms.contenttype.test.DotAssetAPITest.class, + com.dotmarketing.startup.runonce.Task05200WorkflowTaskUniqueKeyTest.class, + com.dotmarketing.portlets.contentlet.transform.WidgetViewStrategyTest.class, + com.dotcms.telemetry.collectors.experiment.CountPagesWithDraftExperimentsMetricTypeTest.class, + com.dotcms.auth.providers.saml.v1.SAMLHelperTest.class, + com.dotcms.analytics.track.collectors.SyncVanitiesCollectorTest.class, + com.dotcms.analytics.track.collectors.AsyncVanitiesCollectorTest.class, + com.dotmarketing.business.LayoutAPITest.class, + com.dotmarketing.portlets.workflows.actionlet.SaveContentActionletTest.class, + com.dotmarketing.business.PermissionBitFactoryImplTest.class, + com.dotcms.rest.api.v1.contenttype.ContentTypeResourceUpdateMetadataTest.class, + com.dotcms.analytics.track.collectors.FilesCollectorTest.class, + com.dotmarketing.startup.runonce.Task210901UpdateDateTimezonesTest.class, + com.dotcms.rest.api.v1.drive.ContentDriveWorkflowArchiveStepTest.class, + com.dotcms.ai.workflow.OpenAIContentPromptActionletTest.class, + com.dotmarketing.portlets.workflows.actionlet.MoveContentActionletTest.class, + com.dotcms.uuid.shorty.LegacyShortyIdApiTest.class, + com.dotcms.publisher.business.PublishQueueElementTransformerTest.class, + com.dotmarketing.startup.runonce.Task05210CreateDefaultDotAssetTest.class, + com.dotcms.security.multipart.ContentDispositionFileNameParserTest.class, + com.dotcms.rest.api.v1.pushpublish.PushPublishFilterResourceTest.class, + com.dotmarketing.startup.runonce.Task05170DefineFrontEndAndBackEndRolesTest.class, com.dotcms.rendering.velocity.VelocityMacroCacheTest.class, - com.dotcms.rendering.velocity.VelocityUtilTest.class, - com.dotcms.rendering.velocity.viewtools.navigation.NavToolTest.class, - com.dotcms.rendering.velocity.viewtools.navigation.NavToolCacheTest.class, - com.dotcms.rendering.velocity.viewtools.content.ContentMapTest.class, - com.dotcms.rendering.velocity.viewtools.content.ContentToolTest.class, - com.dotcms.rendering.velocity.viewtools.ContentSearchToolTest.class, - com.dotmarketing.sitesearch.viewtool.SiteSearchWebAPITest.class, - com.dotcms.rendering.velocity.viewtools.WorkflowToolTest.class, - com.dotcms.rendering.velocity.viewtools.WebsiteToolTest.class, - com.dotcms.rendering.velocity.viewtools.LanguageWebAPITest.class, - com.dotcms.rendering.velocity.viewtools.ContainerWebAPIIntegrationTest.class, - com.dotcms.rendering.velocity.services.VelocityResourceKeyTest.class, - com.dotcms.rendering.velocity.services.HTMLPageAssetRenderedTest.class, - com.dotcms.uuid.shorty.ShortyIdApiTest.class, - DotGraphQLHttpServletTest.class, - com.dotcms.graphql.datafetcher.page.VanityURLFetcherTest.class, - com.dotcms.graphql.datafetcher.page.RunningExperimentFetcherTest.class, - com.dotcms.graphql.datafetcher.CategoryFieldDataFetcherTest.class, - com.dotcms.graphql.datafetcher.FolderCollectionDataFetcherTest.class, - com.dotcms.rest.TagResourceIntegrationTest.class, - com.dotcms.rest.api.v2.tags.TagResourceIntegrationTest.class, - com.dotcms.rest.MapToContentletPopulatorTest.class, - com.dotcms.rest.WebResourceIntegrationTest.class, + com.dotmarketing.util.PageModeTest.class, + com.dotmarketing.servlets.ajax.AjaxDirectorServletIntegrationTest.class, com.dotcms.rest.api.v1.company.CompanyResourceIntegrationTest.class, - com.dotcms.rest.api.v1.configuration.ConfigurationResourceTest.class, - com.dotcms.rest.api.v1.page.NavResourceTest.class, - com.dotcms.rest.api.v1.page.PageResourceTest.class, - com.dotcms.rest.api.v1.page.PageRenderSourcesResourceTest.class, - com.dotcms.rest.api.v1.temp.TempFileResourceTest.class, - com.dotcms.rest.api.v1.content.ContentVersionResourceIntegrationTest.class, - com.dotcms.rest.api.v1.content.ContentResourceIntegrationTest.class, - com.dotcms.rest.api.v1.container.ContainerResourceIntegrationTest.class, - com.dotcms.rest.api.v1.container.ContainerResourceHostResolutionIT.class, - com.dotcms.rest.api.v1.theme.ThemeResourceIntegrationTest.class, - com.dotcms.rest.api.v1.vtl.VTLResourceIntegrationTest.class, - com.dotcms.rest.api.v1.contenttype.ContentTypeResourceIssue15124Test.class, - com.dotcms.rest.api.v1.contenttype.FieldResourceTest.class, - com.dotcms.rest.api.v1.contenttype.ContentTypeResourceTest.class, - Chainable404StorageCacheTest.class, - FileStorageAPITest.class, - com.dotcms.analytics.metrics.QueryParameterValuesTransformerTest.class, - QuartzUtilsTest.class, - DotConnectTest.class, - com.dotcms.contenttype.model.field.layout.FieldUtilTest.class, - com.dotmarketing.portlets.contentlet.business.HostAPITest.class, - com.dotcms.content.elasticsearch.business.IndiciesFactoryTest.class, - com.dotcms.content.elasticsearch.business.ESIndexSpeedTest.class, - com.dotcms.content.elasticsearch.business.ES6UpgradeTest.class, - com.dotcms.content.elasticsearch.business.ESContentFactoryImplTest.class, - com.dotcms.graphql.datafetcher.page.ContentMapDataFetcherTest.class, - com.dotcms.graphql.datafetcher.RelationshipFieldDataFetcherTest.class, - com.dotcms.rest.StoryBlockMarkdownPopulatorTest.class + com.dotcms.enterprise.publishing.remote.handler.ContentWorkflowHandlerTest.class, + com.dotmarketing.quartz.job.PopulateContentletAsJSONJobTest.class, + com.dotmarketing.startup.runonce.Task05030UpdateSystemContentTypesHostTest.class, + com.dotmarketing.quartz.job.IntegrityDataGenerationJobTest.class, + com.dotcms.cost.RequestCostReportTest.class, + com.dotcms.rendering.velocity.viewtools.XmlToolTest.class, + com.dotmarketing.startup.runonce.Task251212AddVersionColumnIndicesTableTest.class, + com.dotcms.business.SystemAPITest.class, + com.dotmarketing.startup.runonce.Task04335CreateSystemWorkflowTest.class, + com.dotcms.dotpubsub.RedisPubSubImplTest.class, + com.dotcms.tika.TikaUtilsTest.class, + com.dotcms.enterprise.publishing.remote.bundler.LinkBundlerTest.class, + com.dotcms.contenttype.test.KeyValueFieldUtilTest.class, + com.dotmarketing.business.IdentifierCacheImplTest.class, + com.dotcms.publisher.endpoint.business.PublishingEndPointFactoryImplTest.class, + com.dotmarketing.startup.runonce.Task240102AlterVarcharLengthOfRelationTypeTest.class, + com.dotmarketing.startup.runonce.Task05225RemoveLoadRecordsToIndexTest.class, + com.dotmarketing.startup.runonce.Task210510UpdateStorageTableDropMetadataColumnTest.class, + com.dotmarketing.startup.runonce.Task220928AddLookbackWindowColumnToExperimentTest.class, + com.dotmarketing.startup.runonce.Task05305AddPushPublishFilterColumnTest.class, + com.dotmarketing.portlets.rules.conditionlet.NumberOfTimesPreviouslyVisitedConditionletTest.class, + com.dotmarketing.portlets.workflows.model.WorkflowSearcherTest.class, + com.dotcms.rest.api.CorsFilterTest.class, + com.dotcms.business.LocalTransactionAndCloseDBIfOpenedFactoryTest.class, + com.dotmarketing.startup.runonce.Task05195CreatesDestroyActionAndAssignDestroyDefaultActionsToTheSystemWorkflowTest.class, + com.dotcms.publishing.BundlerUtilTest.class, + com.dotcms.security.multipart.BoundedBufferedReaderTest.class }) - public class MainSuite1b { } diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite2a.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite2a.java index e38d6e6c3b25..d47e48f30a57 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite2a.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite2a.java @@ -1,122 +1,113 @@ package com.dotcms; -import com.dotcms.ai.workflow.OpenAIAutoTagActionletTest; -import com.dotcms.business.interceptor.InterceptorHandlerTest; -import com.dotcms.content.elasticsearch.util.ESMappingUtilHelperTest; -import com.dotcms.contenttype.business.DotAssetBaseTypeToContentTypeStrategyImplTest; -import com.dotcms.contenttype.test.DotAssetAPITest; -import com.dotcms.dotpubsub.PostgresPubSubImplTest; -import com.dotcms.ema.EMAWebInterceptorTest; -import com.dotcms.enterprise.cluster.ClusterFactoryTest; import com.dotcms.junit.MainBaseSuite; -import com.dotcms.mock.request.CachedParameterDecoratorTest; -import com.dotcms.publisher.bundle.business.BundleFactoryTest; -import com.dotcms.publisher.business.PublishAuditAPITest; -import com.dotcms.publisher.util.PushedAssetUtilTest; -import com.dotcms.publishing.PublisherFilterImplTest; -import com.dotcms.publishing.PushPublishFiltersInitializerTest; -import com.dotcms.rendering.velocity.directive.DotParseTest; -import com.dotcms.rendering.velocity.servlet.VelocityServletIntegrationTest; -import com.dotcms.rest.BundleResourceTest; -import com.dotcms.rest.api.v1.apps.AppsResourceTest; -import com.dotcms.rest.api.v1.folder.FolderResourceTest; -import com.dotcms.rest.api.v1.maintenance.MaintenanceResourceIntegrationTest; -import com.dotcms.rest.api.v1.pushpublish.PushPublishFilterResourceTest; -import com.dotcms.rest.api.v1.user.UserResourceIntegrationTest; -import com.dotcms.saml.IdentityProviderConfigurationFactoryTest; -import com.dotcms.saml.SamlConfigurationServiceTest; -import com.dotcms.security.apps.AppsAPIImplTest; -import com.dotcms.security.apps.AppsCacheImplTest; -import com.dotcms.translate.GoogleTranslationServiceIntegrationTest; -import com.dotmarketing.image.focalpoint.FocalPointAPITest; -import com.dotmarketing.portlets.cmsmaintenance.factories.CMSMaintenanceFactoryTest; -import com.dotmarketing.portlets.containers.business.ContainerFactoryImplTest; -import com.dotmarketing.portlets.containers.business.ContainerStructureFinderStrategyResolverTest; -import com.dotmarketing.portlets.contentlet.model.IntegrationResourceLinkTest; -import com.dotmarketing.portlets.fileassets.business.FileAssetAPIImplIntegrationTest; -import com.dotmarketing.portlets.fileassets.business.FileAssetFactoryIntegrationTest; -import com.dotmarketing.portlets.folders.model.FolderTest; -import com.dotmarketing.portlets.templates.business.TemplateFactoryImplTest; -import com.dotmarketing.portlets.workflows.actionlet.PushNowActionletTest; -import com.dotmarketing.portlets.workflows.model.TestWorkflowAction; -import com.dotmarketing.quartz.job.CleanUpFieldReferencesJobTest; -import com.dotmarketing.startup.runonce.Task05225RemoveLoadRecordsToIndexTest; -import com.dotmarketing.startup.runonce.Task05305AddPushPublishFilterColumnTest; -import com.dotmarketing.startup.runonce.Task05350AddDotSaltClusterColumnTest; -import com.dotmarketing.startup.runonce.Task240131UpdateLanguageVariableContentTypeTest; -import com.dotmarketing.util.HashBuilderTest; -import com.dotmarketing.util.TestConfig; -import com.liferay.portal.language.LanguageUtilTest; -import org.apache.felix.framework.OSGIUtilTest; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; -/* grep -l -r "@Test" dotCMS/src/integration-test */ -/* ./gradlew integrationTest -Dtest.single=com.dotcms.MainSuite */ - /** - * NOTE: LET'S AVOID ADDING MORE TESTS TO THIS SUITE, THIS ONE IS TAKING ALMOST TWICE THE TIME TO RUN THAN THE OTHERS + * Integration test suite shard 3 of 7. + * + * Shards are balanced on measured per-class test time so the slowest shard + * bounds the CI critical path as tightly as possible. When adding a test, + * put it in the shard with the lowest total time rather than appending here + * by habit - see .github/test-matrix.yml for the shard list. + * + * Classes are fully qualified so that rebalancing does not churn imports. */ @RunWith(MainBaseSuite.class) @SuiteClasses({ // Data-scanning tests run FIRST on purpose. // Integration tests accumulate content and never clean up, so anything - // that walks the whole dataset (findAllContent) costs O(all content - // created so far). Scheduled late these pay for every preceding test's - // leftovers. Keep new full-scan tests in this block. - com.dotmarketing.factories.MultiTreeAPITest.class, + // that walks the whole dataset (executeUpgrade, findAll*) costs + // O(all content created so far). Scheduled late these pay for every + // preceding test's leftovers. Keep new full-scan tests in this block. + com.dotcms.content.elasticsearch.business.ContentletIndexAPIImplMappingTimeoutIT.class, - com.dotcms.rest.api.v1.workflow.WorkflowResourceResponseCodeIntegrationTest.class, - com.dotcms.rest.api.v1.workflow.WorkflowResourceIntegrationTest.class, - com.dotcms.rest.api.v1.workflow.WorkflowResourceLicenseIntegrationTest.class, - com.dotcms.rest.api.v1.authentication.ResetPasswordResourceIntegrationTest.class, - com.dotcms.rest.api.v1.authentication.CreateJsonWebTokenResourceIntegrationTest.class, - com.dotcms.rest.api.v1.relationships.RelationshipsResourceTest.class, - com.dotcms.rest.api.v1.contenttype.ContentTypeResourceUpdateMetadataTest.class, - com.dotcms.rest.api.v2.contenttype.FieldResourceTest.class, - com.dotcms.rest.api.v3.contenttype.FieldResourceTest.class, - com.dotcms.rest.api.v3.contenttype.MoveFieldFormTest.class, - com.dotcms.rest.api.CorsFilterTest.class, - com.dotcms.rest.elasticsearch.ESContentResourcePortletTest.class, - com.dotcms.filters.VanityUrlFilterTest.class, - com.dotcms.vanityurl.business.VanityUrlAPITest.class, - com.dotmarketing.portlets.fileassets.business.FileAssetAPITest.class, - com.dotmarketing.portlets.languagesmanager.business.LanguageAPITest.class, - com.dotmarketing.portlets.languagesmanager.business.LanguageFactoryIntegrationTest.class, - com.dotmarketing.portlets.linkchecker.business.LinkCheckerAPITest.class, - com.dotmarketing.portlets.contentlet.util.ContentletUtilTest.class, - com.dotmarketing.portlets.contentlet.business.ContentletCheckInTest.class, - com.dotmarketing.portlets.contentlet.business.ContentletFactoryTest.class, - ContainerStructureFinderStrategyResolverTest.class, - com.dotmarketing.portlets.contentlet.business.ContentletAPITest.class, + com.dotcms.browser.BrowserAPITest.class, + com.dotcms.contenttype.business.ContentTypeDestroyAPIImplTest.class, + com.dotmarketing.portlets.contentlet.business.HostAPITest.class, + com.dotcms.contenttype.test.ContentResourceTest.class, com.dotmarketing.portlets.contentlet.model.ContentletIntegrationTest.class, - com.dotmarketing.portlets.contentlet.transform.BinaryToMapTransformerTest.class, - com.dotmarketing.portlets.contentlet.transform.ContentletTransformerTest.class, - com.dotmarketing.portlets.contentlet.transform.WidgetViewStrategyTest.class, - com.dotmarketing.portlets.contentlet.ajax.ContentletAjaxTest.class, - com.dotmarketing.portlets.workflows.business.SaveContentDraftActionletTest.class, - com.dotmarketing.portlets.workflows.business.WorkflowFactoryTest.class, - com.dotmarketing.portlets.workflows.business.SaveContentActionletTest.class, - com.dotmarketing.portlets.workflows.business.WorkflowAPIMultiLanguageTest.class, - com.dotmarketing.portlets.workflows.business.WorkflowAPITest.class, - com.dotmarketing.portlets.workflows.model.WorkflowSearcherTest.class, - com.dotmarketing.portlets.workflows.model.SystemActionWorkflowActionMappingTest.class, - com.dotmarketing.portlets.workflows.actionlet.FourEyeApproverActionletTest.class, - com.dotmarketing.portlets.workflows.actionlet.SaveContentActionletTest.class, - com.dotmarketing.portlets.workflows.actionlet.SaveContentActionletWithTagsTest.class, - com.dotmarketing.portlets.workflows.actionlet.CopyActionletTest.class, - com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionletTest.class, + com.dotcms.languagevariable.business.LanguageVariableAPITest.class, + com.dotmarketing.quartz.job.DropOldContentVersionsJobTest.class, + com.dotcms.experiments.business.ExperimentUrlPatternCalculatorIntegrationTest.class, + com.dotcms.rest.api.v1.versionable.VersionableResourceTest.class, + com.dotcms.enterprise.publishing.remote.PushPublishBundleGeneratorTest.class, + com.dotmarketing.portlets.fileassets.business.FileAssetAPITest.class, + com.dotcms.content.elasticsearch.business.ESSiteSearchAPITest.class, + com.dotcms.contenttype.test.ContentTypeResourceTest.class, + com.dotcms.rest.api.v1.authentication.ResetPasswordTokenUtilTest.class, + com.dotcms.contenttype.test.FieldFactoryImplTest.class, + com.dotcms.rest.api.v1.folder.FolderResourceSearchTest.class, + com.dotcms.keyvalue.business.KeyValueAPITest.class, + com.dotcms.publisher.business.PublisherAPIImplTest.class, + com.dotcms.experiments.business.IndexRegexUrlPatterStrategyIntegrationTest.class, com.dotmarketing.portlets.personas.business.PersonaAPITest.class, - com.dotmarketing.portlets.personas.business.DeleteMultiTreeUsedPersonaTagJobTest.class, - com.dotmarketing.portlets.links.business.MenuLinkAPITest.class, - com.dotmarketing.portlets.links.factories.LinkFactoryTest.class, - com.dotmarketing.portlets.categories.business.CategoryAPITest.class, - com.dotmarketing.filters.FiltersTest.class, - InterceptorHandlerTest.class, - com.dotcms.graphql.datafetcher.page.NumberContentsDataFetcherTest.class, - com.dotcms.rest.AuditPublishingResourceTest.class, - MaintenanceResourceIntegrationTest.class + com.dotcms.graphql.datafetcher.RelationshipFieldDataFetcherTest.class, + com.dotcms.ai.viewtool.SearchToolTest.class, + com.dotcms.graphql.datafetcher.page.VanityURLFetcherTest.class, + com.dotmarketing.startup.runalways.Task00050LoadAppsSecretsTest.class, + com.dotcms.contenttype.test.ContentTypeImportExportTest.class, + com.dotcms.integritycheckers.ContentPageIntegrityCheckerTest.class, + com.dotmarketing.portlets.fileassets.business.FileAssetAPIImplIntegrationTest.class, + com.dotcms.csspreproc.CSSCacheTest.class, + com.dotcms.analytics.track.collectors.PageDetailCollectorTest.class, + com.dotcms.telemetry.collectors.experiment.CountVariantsInAllRunningExperimentsMetricTypeTest.class, + com.dotcms.integritycheckers.ContentFileAssetIntegrityCheckerTest.class, + com.dotcms.rest.api.v1.drive.ContentDriveHelperContentletAPIComparisonTest.class, + com.dotmarketing.portlets.structure.transform.ContentletRelationshipsTransformerTest.class, + com.dotcms.notification.business.NotificationAPITest.class, + com.dotcms.jitsu.validators.AnalyticsValidatorUtilTest.class, + com.dotcms.rest.api.v1.temp.TempFileResourceTest.class, + com.dotmarketing.business.portal.PortletAPIImplTest.class, + com.dotcms.rest.api.v1.workflow.WorkflowResourceResponseCodeIntegrationTest.class, + com.dotcms.rendering.velocity.VelocityUtilTest.class, + com.dotmarketing.image.focalpoint.FocalPointAPITest.class, + com.dotmarketing.business.IdentifierConsistencyIntegrationTest.class, + com.dotcms.rest.api.v1.menu.MenuResourceTest.class, + com.dotcms.publishing.manifest.CSVManifestReaderTest.class, + com.dotmarketing.common.db.ParamsSetterTest.class, + com.dotcms.enterprise.publishing.remote.bundler.ContentBundlerTest.class, + org.apache.velocity.runtime.parser.node.SimpleNodeTest.class, + com.dotmarketing.portlets.contentlet.action.ImportContentletsActionSmokeTest.class, + com.dotcms.cache.lettuce.RedisClientTest.class, + com.dotmarketing.filters.AutoLoginFilterTest.class, + com.dotmarketing.startup.runonce.Task210218MigrateUserProxyTableTest.class, + com.dotcms.ai.util.ContentToStringUtilTest.class, + com.dotcms.workflow.helper.TestSystemActionMappingsHandlerMerger.class, + com.dotmarketing.startup.runonce.Task220512UpdateNoHTMLRegexValueTest.class, + com.dotcms.rendering.velocity.ASTMethodTest.class, + org.apache.velocity.tools.view.tools.CookieToolTest.class, + com.dotmarketing.servlets.InitRunnerTest.class, + com.dotcms.contenttype.model.field.layout.FieldLayoutRowSerializerTest.class, + com.dotmarketing.portlets.languagesmanager.business.LanguageFactoryIntegrationTest.class, + com.dotmarketing.startup.runonce.Task211012AddCompanyDefaultLanguageTest.class, + com.dotmarketing.startup.runonce.Task251103AddStylePropertiesColumnInMultiTreeTest.class, + com.dotmarketing.startup.runonce.Task210319CreateStorageTableTest.class, + com.dotmarketing.startup.StartupTasksExecutorDataTest.class, + com.dotcms.business.bytebuddy.ByteBuddyAdviceWeavingTest.class, + com.dotcms.content.elasticsearch.business.IndiciesFactoryTest.class, + com.dotmarketing.db.DbConnectionFactoryTest.class, + com.dotcms.visitor.filter.logger.VisitorLoggerTest.class, + com.dotmarketing.startup.StartupTasksExecutorTest.class, + com.dotmarketing.startup.runonce.Task240513UpdateContentTypesSystemFieldTest.class, + com.dotmarketing.startup.runonce.Task210316UpdateLayoutIconsTest.class, + com.dotmarketing.startup.runonce.Task220829CreateExperimentsTableTest.class, + com.dotcms.cdi.SimpleDataProviderWeldRunnerInjectionIT.class, + com.dotcms.analytics.attributes.CustomAttributeFactoryTest.class, + com.dotmarketing.cache.FolderCacheImplIntegrationTest.class, + com.dotcms.cache.KeyValueCacheImplTest.class, + com.dotcms.publishing.manifest.ManifestUtilTest.class, + com.dotmarketing.business.IdentifierAPITest.class, + com.dotcms.rendering.velocity.viewtools.MessageToolTest.class, + com.dotcms.rendering.js.JsEngineTest.class, + com.dotmarketing.portlets.rules.conditionlet.ConditionletOSGIFTest.class, + com.dotmarketing.startup.runonce.Task210321RemoveOldMetadataFilesTest.class, + com.dotcms.enterprise.publishing.staticpublishing.AWSS3PublisherTest.class, + com.dotmarketing.portlets.contentlet.model.ContentletDependenciesTest.class, + com.dotmarketing.startup.runonce.Task05160MultiTreeAddPersonalizationColumnAndChangingPKTest.class, + com.dotmarketing.startup.runalways.Task00001LoadSchemaIntegrationTest.class }) public class MainSuite2a { diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java index fd104dbb79dd..c8edc4e135e8 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java @@ -1,569 +1,108 @@ package com.dotcms; -import com.dotcms.ai.app.ConfigServiceTest; -import com.dotcms.ai.client.AIProxyClientTest; -import com.dotcms.ai.listener.EmbeddingContentListenerTest; -import com.dotcms.ai.viewtool.AIViewToolTest; -import com.dotcms.ai.viewtool.CompletionsToolTest; -import com.dotcms.ai.viewtool.EmbeddingsToolTest; -import com.dotcms.ai.viewtool.SearchToolTest; -import com.dotcms.ai.workflow.OpenAIAutoTagActionletTest; -import com.dotcms.ai.workflow.OpenAIContentPromptActionletTest; -import com.dotcms.analytics.attributes.CustomAttributeAPIImplTest; -import com.dotcms.analytics.attributes.CustomAttributeFactoryTest; -import com.dotcms.analytics.bayesian.BayesianAPIImplIT; -import com.dotcms.analytics.track.collectors.AsyncVanitiesCollectorTest; -import com.dotcms.analytics.track.collectors.BasicProfileCollectorTest; -import com.dotcms.analytics.track.collectors.FilesCollectorTest; -import com.dotcms.analytics.track.collectors.PageDetailCollectorTest; -import com.dotcms.analytics.track.collectors.PagesCollectorTest; -import com.dotcms.analytics.track.collectors.SyncVanitiesCollectorTest; -import com.dotcms.analytics.track.collectors.WebEventsCollectorServiceImplTest; -import com.dotcms.api.web.HttpServletRequestImpersonatorTest; -import com.dotcms.auth.providers.saml.v1.DotSamlResourceTest; -import com.dotcms.auth.providers.saml.v1.SAMLHelperTest; -import com.dotcms.business.SystemAPITest; -import com.dotcms.business.SystemTableFactoryTest; -import com.dotcms.cache.lettuce.DotObjectCodecTest; -import com.dotcms.cache.lettuce.LettuceCacheTest; -import com.dotcms.cache.lettuce.RedisClientTest; -import com.dotcms.cdi.SimpleDataProviderWeldRunnerInjectionIT; -import com.dotcms.cdi.SimpleInjectionIT; -import com.dotcms.cdi.SimpleJUnit4InjectionIT; -import com.dotcms.content.business.ObjectMapperTest; -import com.dotcms.content.business.json.ContentletJsonAPITest; -import com.dotcms.content.business.json.LegacyJSONObjectRenderTest; -import com.dotcms.content.elasticsearch.business.ESIndexAPITest; -import com.dotcms.content.elasticsearch.util.ESMappingUtilHelperTest; -import com.dotcms.content.model.hydration.MetadataDelegateTest; -import com.dotcms.contenttype.business.ContentTypeInitializerTest; -import com.dotcms.contenttype.business.DotAssetBaseTypeToContentTypeStrategyImplTest; -import com.dotcms.contenttype.business.FileAssetBaseTypeToContentTypeStrategyImplTest; -import com.dotcms.contenttype.business.StoryBlockAPITest; -import com.dotcms.contenttype.business.uniquefields.extratable.DBUniqueFieldValidationStrategyTest; -import com.dotcms.contenttype.business.uniquefields.extratable.UniqueFieldDataBaseUtilTest; -import com.dotcms.contenttype.test.DotAssetAPITest; -import com.dotcms.csspreproc.CSSCacheTest; -import com.dotcms.csspreproc.CSSPreProcessServletIT; -import com.dotcms.dotpubsub.PostgresPubSubImplTest; -import com.dotcms.dotpubsub.RedisPubSubImplTest; -import com.dotcms.ema.EMAWebInterceptorTest; -import com.dotcms.enterprise.cluster.ClusterFactoryTest; -import com.dotcms.enterprise.publishing.bundler.URLMapBundlerTest; -import com.dotcms.enterprise.publishing.remote.StaticPushPublishBundleGeneratorTest; -import com.dotcms.enterprise.publishing.remote.bundler.ContainerBundlerTest; -import com.dotcms.enterprise.publishing.remote.bundler.ContentBundlerTest; -import com.dotcms.enterprise.publishing.remote.bundler.ContentTypeBundlerTest; -import com.dotcms.enterprise.publishing.remote.bundler.FolderBundlerTest; -import com.dotcms.enterprise.publishing.remote.bundler.HostBundlerTest; -import com.dotcms.enterprise.publishing.remote.bundler.LinkBundlerTest; -import com.dotcms.enterprise.publishing.remote.bundler.TemplateBundlerTest; -import com.dotcms.enterprise.publishing.remote.bundler.WorkflowBundlerTest; -import com.dotcms.enterprise.publishing.remote.handler.ContentHandlerTest; -import com.dotcms.enterprise.publishing.remote.handler.ContentWorkflowHandlerTest; -import com.dotcms.enterprise.publishing.remote.handler.HandlerUtilTest; -import com.dotcms.enterprise.publishing.staticpublishing.AWSS3PublisherTest; -import com.dotcms.enterprise.publishing.staticpublishing.LanguageFolderTest; -import com.dotcms.experiments.business.IndexRegexUrlPatterStrategyIntegrationTest; -import com.dotcms.experiments.business.RootIndexRegexUrlPatterStrategyIntegrationTest; -import com.dotcms.filters.interceptor.meta.MetaWebInterceptorTest; -import com.dotcms.integritycheckers.ContentFileAssetIntegrityCheckerTest; -import com.dotcms.integritycheckers.ContentPageIntegrityCheckerTest; -import com.dotcms.integritycheckers.HostIntegrityCheckerTest; -import com.dotcms.integritycheckers.IntegrityUtilTest; -import com.dotcms.jobs.business.api.JobQueueManagerAPITest; import com.dotcms.junit.MainBaseSuite; -import com.dotcms.mail.MailAPIImplTest; -import com.dotcms.mock.request.CachedParameterDecoratorTest; -import com.dotcms.publisher.bundle.business.BundleAPITest; -import com.dotcms.publisher.bundle.business.BundleFactoryTest; -import com.dotcms.publisher.business.PublishAuditAPITest; -import com.dotcms.publisher.receiver.BundlePublisherTest; -import com.dotcms.publisher.util.DependencyManagerTest; -import com.dotcms.publisher.util.PushedAssetUtilTest; -import com.dotcms.publishing.BundlerUtilTest; -import com.dotcms.publishing.PublisherFilterImplTest; -import com.dotcms.publishing.PushPublishFiltersInitializerTest; -import com.dotcms.publishing.manifest.CSVManifestBuilderTest; -import com.dotcms.publishing.manifest.CSVManifestReaderTest; -import com.dotcms.publishing.manifest.ManifestReaderFactoryTest; -import com.dotcms.publishing.manifest.ManifestUtilTest; -import com.dotcms.rendering.velocity.directive.DotParseTest; -import com.dotcms.rendering.velocity.servlet.VelocityServletIntegrationTest; -import com.dotcms.rendering.velocity.viewtools.DotTemplateToolTest; -import com.dotcms.rendering.velocity.viewtools.FileToolTest; -import com.dotcms.rendering.velocity.viewtools.JSONToolTest; -import com.dotcms.rendering.velocity.viewtools.MessageToolTest; -import com.dotcms.rendering.velocity.viewtools.XmlToolTest; -import com.dotcms.rendering.velocity.viewtools.content.StoryBlockMapTest; -import com.dotcms.rendering.velocity.viewtools.content.StoryBlockTest; -import com.dotcms.rest.BundleResourceTest; -import com.dotcms.rest.api.v1.announcements.AnnouncementsHelperIntegrationTest; -import com.dotcms.rest.api.v1.announcements.RemoteAnnouncementsLoaderIntegrationTest; -import com.dotcms.rest.api.v1.apps.SiteViewPaginatorIntegrationTest; -import com.dotcms.rest.api.v1.apps.view.AppsInterpolationTest; -import com.dotcms.rest.api.v1.asset.AssetPathResolverImplIntegrationTest; -import com.dotcms.rest.api.v1.asset.WebAssetHelperIntegrationTest; -import com.dotcms.rest.api.v1.authentication.ResetPasswordTokenUtilTest; -import com.dotcms.rest.api.v1.folder.FolderResourceSearchTest; -import com.dotcms.rest.api.v1.folder.FolderResourceTest; -import com.dotcms.rest.api.v1.maintenance.ClusterLogCollectorTest; -import com.dotcms.rest.api.v1.menu.MenuResourceTest; -import com.dotcms.rest.api.v1.publishing.BundleManagementResourceIntegrationTest; -import com.dotcms.rest.api.v1.publishing.PublishingResourceIntegrationTest; -import com.dotcms.rest.api.v1.pushpublish.PushPublishFilterResourceTest; -import com.dotcms.rest.api.v1.system.ConfigurationHelperTest; -import com.dotcms.rest.api.v1.system.permission.PermissionResourceIntegrationTest; -import com.dotcms.rest.api.v1.taillog.TailLogResourceTest; -import com.dotcms.rest.api.v1.user.UserResourceIntegrationTest; -import com.dotcms.rest.api.v2.asset.WebAssetResourceV2IntegrationTest; -import com.dotcms.saml.IdentityProviderConfigurationFactoryTest; -import com.dotcms.saml.SamlConfigurationServiceTest; -import com.dotcms.security.apps.AppsCacheImplTest; -import com.dotcms.security.multipart.BoundedBufferedReaderTest; -import com.dotcms.security.multipart.ContentDispositionFileNameParserTest; -import com.dotcms.security.multipart.SecureFileValidatorTest; -import com.dotcms.storage.FileMetadataAPITest; -import com.dotcms.storage.StoragePersistenceAPITest; -import com.dotcms.storage.repository.HashedLocalFileRepositoryManagerTest; -import com.dotcms.timemachine.business.TimeMachineAPITest; -import com.dotcms.translate.GoogleTranslationServiceIntegrationTest; -import com.dotcms.util.content.json.PopulateContentletAsJSONUtilTest; -import com.dotcms.variant.VariantAPITest; -import com.dotcms.variant.VariantFactoryTest; -import com.dotcms.variant.business.VariantCacheTest; -import com.dotmarketing.beans.HostTest; -import com.dotmarketing.business.IdentifierCacheImplTest; -import com.dotmarketing.business.PermissionBitFactoryImplTest; -import com.dotmarketing.business.VersionableFactoryImplTest; -import com.dotmarketing.business.helper.PermissionHelperTest; -import com.dotmarketing.common.db.DBTimeZoneCheckTest; -import com.dotmarketing.filters.AutoLoginFilterTest; -import com.dotmarketing.filters.CMSUrlUtilIntegrationTest; -import com.dotmarketing.image.focalpoint.FocalPointAPITest; -import com.dotmarketing.osgi.GenericBundleActivatorIntegrationTest; -import com.dotmarketing.portlets.browser.BrowserUtilTest; -import com.dotmarketing.portlets.browser.ajax.BrowserAjaxTest; -import com.dotmarketing.portlets.categories.business.CategoryFactoryTest; -import com.dotmarketing.portlets.cmsmaintenance.factories.CMSMaintenanceFactoryTest; -import com.dotmarketing.portlets.containers.business.ContainerFactoryImplTest; -import com.dotmarketing.portlets.contentlet.business.ContentletCacheImplTest; -import com.dotmarketing.portlets.contentlet.model.ContentletDependenciesTest; -import com.dotmarketing.portlets.contentlet.model.IntegrationResourceLinkTest; -import com.dotmarketing.portlets.fileassets.business.FileAssetAPIImplIntegrationTest; -import com.dotmarketing.portlets.fileassets.business.FileAssetFactoryIntegrationTest; -import com.dotmarketing.portlets.folders.business.FolderFactoryImplTest; -import com.dotmarketing.portlets.folders.model.FolderTest; -import com.dotmarketing.portlets.templates.business.FileAssetTemplateUtilTest; -import com.dotmarketing.portlets.templates.business.TemplateFactoryImplTest; -import com.dotmarketing.portlets.workflows.actionlet.MoveContentActionletTest; -import com.dotmarketing.portlets.workflows.actionlet.PushNowActionletTest; -import com.dotmarketing.portlets.workflows.actionlet.SaveContentAsDraftActionletIntegrationTest; -import com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionletAbortTest; -import com.dotmarketing.portlets.workflows.model.TestWorkflowAction; -import com.dotmarketing.quartz.DotStatefulJobTest; -import com.dotmarketing.quartz.job.CleanUpFieldReferencesJobTest; -import com.dotmarketing.quartz.job.DropOldContentVersionsJobTest; -import com.dotmarketing.quartz.job.EncryptPlainPasswordsJobTest; -import com.dotmarketing.quartz.job.IntegrityDataGenerationJobTest; -import com.dotmarketing.quartz.job.PopulateContentletAsJSONJobTest; -import com.dotmarketing.quartz.job.PruneTimeMachineBackupJobTest; -import com.dotmarketing.startup.StartupTasksExecutorDataTest; -import com.dotmarketing.startup.StartupTasksExecutorTest; -import com.dotmarketing.startup.runalways.Task00050LoadAppsSecretsTest; -import com.dotmarketing.startup.runonce.Task05195CreatesDestroyActionAndAssignDestroyDefaultActionsToTheSystemWorkflowTest; -import com.dotmarketing.startup.runonce.Task05210CreateDefaultDotAssetTest; -import com.dotmarketing.startup.runonce.Task05225RemoveLoadRecordsToIndexTest; -import com.dotmarketing.startup.runonce.Task05305AddPushPublishFilterColumnTest; -import com.dotmarketing.startup.runonce.Task05350AddDotSaltClusterColumnTest; -import com.dotmarketing.startup.runonce.Task05370AddAppsPortletToLayoutTest; -import com.dotmarketing.startup.runonce.Task05380ChangeContainerPathToAbsoluteTest; -import com.dotmarketing.startup.runonce.Task05390MakeRoomForLongerJobDetailTest; -import com.dotmarketing.startup.runonce.Task05395RemoveEndpointIdForeignKeyInIntegrityResolverTablesIntegrationTest; -import com.dotmarketing.startup.runonce.Task201013AddNewColumnsToIdentifierTableTest; -import com.dotmarketing.startup.runonce.Task201014UpdateColumnsValuesInIdentifierTableTest; -import com.dotmarketing.startup.runonce.Task201102UpdateColumnSitelicTableTest; -import com.dotmarketing.startup.runonce.Task210218MigrateUserProxyTableTest; -import com.dotmarketing.startup.runonce.Task210319CreateStorageTableTest; -import com.dotmarketing.startup.runonce.Task210321RemoveOldMetadataFilesTest; -import com.dotmarketing.startup.runonce.Task210506UpdateStorageTableTest; -import com.dotmarketing.startup.runonce.Task210510UpdateStorageTableDropMetadataColumnTest; -import com.dotmarketing.startup.runonce.Task210520UpdateAnonymousEmailTest; -import com.dotmarketing.startup.runonce.Task210527DropReviewFieldsFromContentletTableTest; -import com.dotmarketing.startup.runonce.Task210719CleanUpTitleFieldTest; -import com.dotmarketing.startup.runonce.Task210802UpdateStructureTableTest; -import com.dotmarketing.startup.runonce.Task210805DropUserProxyTableTest; -import com.dotmarketing.startup.runonce.Task210816DeInodeRelationshipTest; -import com.dotmarketing.startup.runonce.Task210901UpdateDateTimezonesTest; -import com.dotmarketing.startup.runonce.Task211007RemoveNotNullConstraintFromCompanyMXColumnTest; -import com.dotmarketing.startup.runonce.Task211012AddCompanyDefaultLanguageTest; -import com.dotmarketing.startup.runonce.Task211101AddContentletAsJsonColumnTest; -import com.dotmarketing.startup.runonce.Task211103RenameHostNameLabelTest; -import com.dotmarketing.startup.runonce.Task220202RemoveFKStructureFolderConstraintTest; -import com.dotmarketing.startup.runonce.Task220203RemoveFolderInodeConstraintTest; -import com.dotmarketing.startup.runonce.Task220214AddOwnerAndIDateToFolderTableTest; -import com.dotmarketing.startup.runonce.Task260720AddDefaultBaseTypeToFolderTableTest; -import com.dotmarketing.startup.runonce.Task220215MigrateDataFromInodeToFolderTest; -import com.dotmarketing.startup.runonce.Task220330ChangeVanityURLSiteFieldTypeTest; -import com.dotmarketing.startup.runonce.Task220401CreateClusterLockTableTest; -import com.dotmarketing.startup.runonce.Task220402UpdateDateTimezonesTest; -import com.dotmarketing.startup.runonce.Task220413IncreasePublishedPushedAssetIdColTest; -import com.dotmarketing.startup.runonce.Task220512UpdateNoHTMLRegexValueTest; -import com.dotmarketing.startup.runonce.Task220606UpdatePushNowActionletNameTest; -import com.dotmarketing.startup.runonce.Task220822CreateVariantTableTest; -import com.dotmarketing.startup.runonce.Task220824CreateDefaultVariantTest; -import com.dotmarketing.startup.runonce.Task220825CreateVariantFieldTest; -import com.dotmarketing.startup.runonce.Task220829CreateExperimentsTableTest; -import com.dotmarketing.startup.runonce.Task220912UpdateCorrectShowOnMenuPropertyTest; -import com.dotmarketing.startup.runonce.Task220928AddLookbackWindowColumnToExperimentTest; -import com.dotmarketing.startup.runonce.Task221007AddVariantIntoPrimaryKeyTest; -import com.dotmarketing.startup.runonce.Task230110MakeSomeSystemFieldsRemovableByBaseTypeTest; -import com.dotmarketing.startup.runonce.Task230328AddMarkedForDeletionColumnTest; -import com.dotmarketing.startup.runonce.Task230426AlterVarcharLengthOfLockedByColTest; -import com.dotmarketing.startup.runonce.Task230523CreateVariantFieldInContentletIntegrationTest; -import com.dotmarketing.startup.runonce.Task230701AddHashIndicesToWorkflowTablesTest; -import com.dotmarketing.startup.runonce.Task230707CreateSystemTableTest; -import com.dotmarketing.startup.runonce.Task230713IncreaseDisabledWysiwygColumnSizeTest; -import com.dotmarketing.startup.runonce.Task231109AddPublishDateToContentletVersionInfoTest; -import com.dotmarketing.startup.runonce.Task240102AlterVarcharLengthOfRelationTypeTest; -import com.dotmarketing.startup.runonce.Task240111AddInodeAndIdentifierLeftIndexesTest; -import com.dotmarketing.startup.runonce.Task240112AddMetadataColumnToStructureTableTest; -import com.dotmarketing.startup.runonce.Task240131UpdateLanguageVariableContentTypeTest; -import com.dotmarketing.startup.runonce.Task240513UpdateContentTypesSystemFieldTest; -import com.dotmarketing.startup.runonce.Task240530AddDotAIPortletToLayoutTest; -import com.dotmarketing.startup.runonce.Task240606AddVariableColumnToWorkflowTest; -import com.dotmarketing.startup.runonce.Task241013RemoveFullPathLcColumnFromIdentifierTest; -import com.dotmarketing.startup.runonce.Task241015ReplaceLanguagesWithLocalesPortletTest; -import com.dotmarketing.startup.runonce.Task241016AddCustomLanguageVariablesPortletToLayoutTest; -import com.dotmarketing.startup.runonce.Task250107RemoveEsReadOnlyMonitorJobTest; -import com.dotmarketing.startup.runonce.Task250113CreatePostgresJobQueueTablesTest; -import com.dotmarketing.startup.runonce.Task250828CreateCustomAttributeTableTest; -import com.dotmarketing.util.ConfigUtilsTest; -import com.dotmarketing.util.HashBuilderTest; -import com.dotmarketing.util.ITConfigTest; -import com.dotmarketing.util.MaintenanceUtilTest; -import com.dotmarketing.util.ResourceCollectorUtilTest; -import com.dotmarketing.util.TestConfig; -import com.dotmarketing.util.UtilMethodsITest; -import com.dotmarketing.util.contentlet.pagination.PaginatedContentletsIntegrationTest; -import com.liferay.portal.language.LanguageUtilTest; -import org.apache.felix.framework.OSGIUtilTest; -import org.apache.velocity.tools.view.tools.CookieToolTest; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; -/* grep -l -r "@Test" dotCMS/src/integration-test */ -/* ./gradlew integrationTest -Dtest.single=com.dotcms.MainSuite */ - - +/** + * Integration test suite shard 4 of 7. + * + * Shards are balanced on measured per-class test time so the slowest shard + * bounds the CI critical path as tightly as possible. When adding a test, + * put it in the shard with the lowest total time rather than appending here + * by habit - see .github/test-matrix.yml for the shard list. + * + * Classes are fully qualified so that rebalancing does not churn imports. + */ @RunWith(MainBaseSuite.class) @SuiteClasses({ - // Reindex-heavy tests run FIRST on purpose. - // Integration tests accumulate content and never clean up, so a full - // reindex costs O(all content created so far). Scheduled late in a - // 297-class suite these reindex the entire accumulated dataset instead - // of just their own fixtures. Keep new full-reindex tests in this block. - ESMappingUtilHelperTest.class, + // Data-scanning tests run FIRST on purpose. + // Integration tests accumulate content and never clean up, so anything + // that walks the whole dataset (executeUpgrade, findAll*) costs + // O(all content created so far). Scheduled late these pay for every + // preceding test's leftovers. Keep new full-scan tests in this block. + com.dotmarketing.quartz.job.CleanUpFieldReferencesJobTest.class, com.dotmarketing.common.reindex.ReindexThreadTest.class, - com.dotcms.content.elasticsearch.business.ContentletIndexAPIImplMappingTimeoutIT.class, - com.dotmarketing.common.reindex.ReindexAPITest.class, - CleanUpFieldReferencesJobTest.class, - EMAWebInterceptorTest.class, - Task220825CreateVariantFieldTest.class, - Task221007AddVariantIntoPrimaryKeyTest.class, + com.dotcms.rest.api.v1.page.PageResourceTest.class, + com.dotcms.util.ImportUtilTest.class, + com.dotcms.content.elasticsearch.business.ContentletIndexAPIImplTest.class, + com.dotcms.contenttype.business.StoryBlockAPITest.class, + com.dotcms.rendering.velocity.viewtools.content.ContentToolTest.class, + com.dotcms.rest.MapToContentletPopulatorTest.class, com.dotcms.rest.api.v1.template.TemplateResourceTest.class, - Task05380ChangeContainerPathToAbsoluteTest.class, - DotTemplateToolTest.class, - Task05370AddAppsPortletToLayoutTest.class, - FolderFactoryImplTest.class, - DotSamlResourceTest.class, - DotStatefulJobTest.class, - IntegrityDataGenerationJobTest.class, - BundleAPITest.class, - Task05390MakeRoomForLongerJobDetailTest.class, - Task05395RemoveEndpointIdForeignKeyInIntegrityResolverTablesIntegrationTest.class, - JSONToolTest.class, - Task00050LoadAppsSecretsTest.class, - StoragePersistenceAPITest.class, - FileMetadataAPITest.class, - StartupTasksExecutorTest.class, - Task201013AddNewColumnsToIdentifierTableTest.class, - Task201014UpdateColumnsValuesInIdentifierTableTest.class, - AppsInterpolationTest.class, - Task201102UpdateColumnSitelicTableTest.class, - DependencyManagerTest.class, - com.dotcms.rest.api.v1.versionable.VersionableResourceTest.class, - GenericBundleActivatorIntegrationTest.class, - SAMLHelperTest.class, - PermissionHelperTest.class, - ResetPasswordTokenUtilTest.class, - ContainerBundlerTest.class, - ContentTypeBundlerTest.class, - FolderBundlerTest.class, - HostBundlerTest.class, - LinkBundlerTest.class, - TemplateBundlerTest.class, - WorkflowBundlerTest.class, - AutoLoginFilterTest.class, - Task210218MigrateUserProxyTableTest.class, - com.dotmarketing.startup.runonce.Task210316UpdateLayoutIconsTest.class, - Task210319CreateStorageTableTest.class, - Task210321RemoveOldMetadataFilesTest.class, - DBTimeZoneCheckTest.class, - ContentHandlerTest.class, - ESIndexAPITest.class, - FileAssetTemplateUtilTest.class, - Task210506UpdateStorageTableTest.class, - Task210520UpdateAnonymousEmailTest.class, - Task210510UpdateStorageTableDropMetadataColumnTest.class, - StaticPushPublishBundleGeneratorTest.class, - CookieToolTest.class, - CSVManifestBuilderTest.class, - MoveContentActionletTest.class, - Task210527DropReviewFieldsFromContentletTableTest.class, - ContentletCacheImplTest.class, - HostTest.class, - FileToolTest.class, - Task210719CleanUpTitleFieldTest.class, - Task210802UpdateStructureTableTest.class, - MaintenanceUtilTest.class, - BundlePublisherTest.class, - CategoryFactoryTest.class, - Task210805DropUserProxyTableTest.class, - Task210816DeInodeRelationshipTest.class, - ConfigurationHelperTest.class, - CSVManifestReaderTest.class, - Task210901UpdateDateTimezonesTest.class, - DotObjectCodecTest.class, - RedisClientTest.class, - LettuceCacheTest.class, - RedisPubSubImplTest.class, - ManifestReaderFactoryTest.class, - ResourceCollectorUtilTest.class, - Task211007RemoveNotNullConstraintFromCompanyMXColumnTest.class, - Task211012AddCompanyDefaultLanguageTest.class, - HostIntegrityCheckerTest.class, - MetaWebInterceptorTest.class, - BrowserUtilTest.class, - Task211101AddContentletAsJsonColumnTest.class, - ContentletJsonAPITest.class, - VelocityScriptActionletAbortTest.class, - StoryBlockMapTest.class, - HandlerUtilTest.class, - Task211103RenameHostNameLabelTest.class, - MessageToolTest.class, - XmlToolTest.class, - LanguageFolderTest.class, - MailAPIImplTest.class, - CSSCacheTest.class, + com.dotcms.publisher.business.PublisherTest.class, + com.dotcms.content.elasticsearch.business.ESMappingAPITest.class, + org.apache.felix.framework.OSGIUtilTest.class, + com.dotmarketing.portlets.contentlet.business.web.ContentletWebAPIImplIntegrationTest.class, + com.dotcms.contenttype.business.uniquefields.extratable.DBUniqueFieldValidationStrategyTest.class, + com.dotcms.rest.api.v1.theme.ThemeResourceIntegrationTest.class, com.dotcms.rendering.velocity.viewtools.content.BinaryMapTest.class, - IntegrityUtilTest.class, - Task220202RemoveFKStructureFolderConstraintTest.class, - ContentBundlerTest.class, - ObjectMapperTest.class, - URLMapBundlerTest.class, - PermissionBitFactoryImplTest.class, - Task220203RemoveFolderInodeConstraintTest.class, - Task220214AddOwnerAndIDateToFolderTableTest.class, - Task260720AddDefaultBaseTypeToFolderTableTest.class, - Task220215MigrateDataFromInodeToFolderTest.class, - Task220330ChangeVanityURLSiteFieldTypeTest.class, - Task220402UpdateDateTimezonesTest.class, - Task220413IncreasePublishedPushedAssetIdColTest.class, - com.dotcms.util.pagination.ContainerPaginatorTest.class, - ContentDispositionFileNameParserTest.class, - SecureFileValidatorTest.class, - BoundedBufferedReaderTest.class, - ContentWorkflowHandlerTest.class, - Task220512UpdateNoHTMLRegexValueTest.class, - MetadataDelegateTest.class, - Task220401CreateClusterLockTableTest.class, - Task220606UpdatePushNowActionletNameTest.class, - BundlerUtilTest.class, - MenuResourceTest.class, - AWSS3PublisherTest.class, - ContentTypeInitializerTest.class, - CSSPreProcessServletIT.class, - VariantFactoryTest.class, - VariantAPITest.class, - PaginatedContentletsIntegrationTest.class, - Task220824CreateDefaultVariantTest.class, - Task220822CreateVariantTableTest.class, - Task220829CreateExperimentsTableTest.class, - StoryBlockTest.class, - IdentifierCacheImplTest.class, - VariantCacheTest.class, - VersionableFactoryImplTest.class, - Task220928AddLookbackWindowColumnToExperimentTest.class, - TailLogResourceTest.class, - ClusterLogCollectorTest.class, - BayesianAPIImplIT.class, - ContentletDependenciesTest.class, - SaveContentAsDraftActionletIntegrationTest.class, - StoryBlockAPITest.class, - UtilMethodsITest.class, - Task220912UpdateCorrectShowOnMenuPropertyTest.class, - HashedLocalFileRepositoryManagerTest.class, - ManifestUtilTest.class, - Task230110MakeSomeSystemFieldsRemovableByBaseTypeTest.class, - BrowserAjaxTest.class, - PopulateContentletAsJSONUtilTest.class, - PopulateContentletAsJSONJobTest.class, - Task230328AddMarkedForDeletionColumnTest.class, - StartupTasksExecutorDataTest.class, - Task230426AlterVarcharLengthOfLockedByColTest.class, - AssetPathResolverImplIntegrationTest.class, - WebAssetHelperIntegrationTest.class, - WebAssetResourceV2IntegrationTest.class, - SystemTableFactoryTest.class, - Task230707CreateSystemTableTest.class, - SystemAPITest.class, - Task230701AddHashIndicesToWorkflowTablesTest.class, - Task230713IncreaseDisabledWysiwygColumnSizeTest.class, - ContentPageIntegrityCheckerTest.class, - IndexRegexUrlPatterStrategyIntegrationTest.class, - RootIndexRegexUrlPatterStrategyIntegrationTest.class, - SiteViewPaginatorIntegrationTest.class, - Task230523CreateVariantFieldInContentletIntegrationTest.class, - DropOldContentVersionsJobTest.class, - Task231109AddPublishDateToContentletVersionInfoTest.class, - Task240102AlterVarcharLengthOfRelationTypeTest.class, - Task240111AddInodeAndIdentifierLeftIndexesTest.class, - AnnouncementsHelperIntegrationTest.class, - RemoteAnnouncementsLoaderIntegrationTest.class, - Task240112AddMetadataColumnToStructureTableTest.class, - AIViewToolTest.class, - SearchToolTest.class, - EmbeddingsToolTest.class, - CompletionsToolTest.class, - ConfigServiceTest.class, - AIProxyClientTest.class, - TimeMachineAPITest.class, - Task240513UpdateContentTypesSystemFieldTest.class, - PruneTimeMachineBackupJobTest.class, - CMSUrlUtilIntegrationTest.class, - ContentFileAssetIntegrityCheckerTest.class, - ITConfigTest.class, - Task240530AddDotAIPortletToLayoutTest.class, - EmbeddingContentListenerTest.class, - Task240606AddVariableColumnToWorkflowTest.class, - OpenAIContentPromptActionletTest.class, - JobQueueManagerAPITest.class, - ConfigUtilsTest.class, - SimpleInjectionIT.class, - SimpleDataProviderWeldRunnerInjectionIT.class, - SimpleJUnit4InjectionIT.class, - LegacyJSONObjectRenderTest.class, - Task241013RemoveFullPathLcColumnFromIdentifierTest.class, - Task250113CreatePostgresJobQueueTablesTest.class, - UniqueFieldDataBaseUtilTest.class, - DBUniqueFieldValidationStrategyTest.class, - Task241015ReplaceLanguagesWithLocalesPortletTest.class, - Task241016AddCustomLanguageVariablesPortletToLayoutTest.class, - WebEventsCollectorServiceImplTest.class, - BasicProfileCollectorTest.class, - PagesCollectorTest.class, - PageDetailCollectorTest.class, - FilesCollectorTest.class, - SyncVanitiesCollectorTest.class, - AsyncVanitiesCollectorTest.class, - HttpServletRequestImpersonatorTest.class, - Task250107RemoveEsReadOnlyMonitorJobTest.class, - com.dotmarketing.business.VersionableAPITest.class, + com.dotcms.content.elasticsearch.business.ES6UpgradeTest.class, + com.dotcms.rest.api.v2.tags.TagResourceIntegrationTest.class, + com.dotcms.ai.api.OpenAIVisionAPIImplTest.class, com.dotmarketing.business.UserAPITest.class, - com.dotmarketing.business.portal.PortletAPIImplTest.class, - com.dotmarketing.business.web.LanguageWebApiTest.class, - com.dotmarketing.business.IdentifierFactoryTest.class, - com.dotmarketing.business.IdentifierAPITest.class, - com.dotmarketing.business.CommitListenerCacheWrapperTest.class, - com.dotmarketing.business.RoleAPITest.class, - com.dotmarketing.business.IdentifierConsistencyIntegrationTest.class, - com.dotmarketing.business.LayoutAPITest.class, - com.dotmarketing.business.PermissionAPIIntegrationTest.class, - com.dotmarketing.business.PermissionAPITest.class, - com.dotmarketing.servlets.BinaryExporterServletTest.class, - com.dotmarketing.servlets.ShortyServletAndTitleImageTest.class, - com.dotmarketing.servlets.InitRunnerTest.class, - com.dotmarketing.servlets.ajax.AjaxDirectorServletIntegrationTest.class, - FocalPointAPITest.class, - com.dotmarketing.tag.business.TagAPITest.class, - OSGIUtilTest.class, - EncryptPlainPasswordsJobTest.class, - CachedParameterDecoratorTest.class, - ContainerFactoryImplTest.class, - TemplateFactoryImplTest.class, - TestConfig.class, - FolderTest.class, - PublishAuditAPITest.class, - BundleFactoryTest.class, - com.dotcms.security.apps.SecretsStoreKeyStoreImplTest.class, - AppsCacheImplTest.class, - VelocityServletIntegrationTest.class, + com.dotcms.rest.api.v1.announcements.AnnouncementsHelperIntegrationTest.class, + com.dotmarketing.portlets.linkchecker.business.LinkCheckerAPITest.class, + com.dotcms.contenttype.business.FileAssetBaseTypeToContentTypeStrategyImplTest.class, + com.dotcms.publishing.remote.RemoteReceiverLanguageResolutionTest.class, + com.dotmarketing.portlets.structure.model.ContentletRelationshipsTest.class, + com.dotmarketing.portlets.cmsmaintenance.factories.CMSMaintenanceFactoryTest.class, + com.dotcms.rest.api.v1.content.search.strategies.GlobalSearchAttributeStrategyMatchingTest.class, + com.dotmarketing.portlets.contentlet.model.IntegrationResourceLinkTest.class, + com.dotcms.ai.viewtool.AIViewToolTest.class, + com.dotcms.telemetry.collectors.experiment.CountVariantsInAllEndedExperimentsMetricTypeTest.class, + com.dotmarketing.startup.runonce.Task250604UpdateFolderInodesTest.class, + com.dotcms.rendering.velocity.viewtools.DotTemplateToolTest.class, + com.dotmarketing.startup.runonce.Task220330ChangeVanityURLSiteFieldTypeTest.class, + com.dotcms.rendering.velocity.viewtools.ContentSearchToolTest.class, + com.dotcms.rendering.velocity.viewtools.XsltToolTest.class, + com.dotcms.telemetry.collectors.theme.TotalSizeOfFilesPerThemeMetricTypeTest.class, + com.dotcms.contenttype.test.FieldBuilderTest.class, + com.dotcms.rest.api.v1.drive.ContentDriveWorkflowFilterTest.class, + com.dotcms.rest.api.v1.maintenance.ClusterLogCollectorTest.class, + com.dotmarketing.portlets.workflows.actionlet.SaveContentActionletWithTagsTest.class, + com.dotcms.contenttype.test.ContentTypeBuilderTest.class, + com.dotmarketing.portlets.workflows.business.WorkflowAPIMultiLanguageTest.class, + com.dotcms.publisher.endpoint.bean.PublishingEndPointTest.class, + com.dotcms.integritycheckers.HostIntegrityCheckerTest.class, + com.dotmarketing.startup.runonce.Task220825CreateVariantFieldTest.class, + com.dotmarketing.startup.runonce.Task240606AddVariableColumnToWorkflowTest.class, + com.dotcms.variant.VariantFactoryTest.class, + com.dotcms.rest.WebResourceIntegrationTest.class, + com.dotcms.rest.api.v1.relationships.RelationshipsResourceTest.class, + com.dotcms.contenttype.model.field.layout.FieldLayoutColumnSerializerTest.class, + com.dotcms.saml.IdentityProviderConfigurationFactoryTest.class, + com.dotmarketing.startup.runonce.Task210816DeInodeRelationshipTest.class, + com.dotmarketing.portlets.rules.business.RulesCacheFTest.class, + com.dotcms.integritycheckers.FolderIntegrityCheckerTest.class, + com.dotcms.enterprise.publishing.remote.bundler.ContainerBundlerTest.class, + com.dotcms.publishing.manifest.ManifestReaderFactoryTest.class, + com.dotcms.publishing.manifest.CSVManifestBuilderTest.class, + com.liferay.portal.language.LanguageUtilTest.class, com.dotmarketing.common.db.DotDatabaseMetaDataTest.class, - com.dotmarketing.common.db.ParamsSetterTest.class, - com.dotmarketing.cms.urlmap.URLMapAPIImplTest.class, - com.dotmarketing.factories.PublishFactoryTest.class, - com.dotmarketing.factories.WebAssetFactoryTest.class, - com.dotmarketing.db.DbConnectionFactoryTest.class, - com.dotmarketing.db.DbConnectionFactoryUtilTest.class, - com.dotmarketing.db.HibernateUtilTest.class, + com.dotmarketing.startup.runonce.Task211101AddContentletAsJsonColumnTest.class, + com.dotcms.enterprise.publishing.remote.bundler.WorkflowBundlerTest.class, com.dotmarketing.quartz.job.BinaryCleanupJobTest.class, - - com.dotmarketing.fixTasks.FixTask00085FixEmptyParentPathOnIdentifierTest.class, - com.dotmarketing.startup.runonce.Task05170DefineFrontEndAndBackEndRolesTest.class, - com.dotmarketing.startup.runonce.Task04375UpdateCategoryKeyTest.class, - com.dotmarketing.startup.runonce.Task04335CreateSystemWorkflowTest.class, - com.dotmarketing.startup.runonce.Task04375UpdateColorsTest.class, - com.dotmarketing.startup.runonce.Task05160MultiTreeAddPersonalizationColumnAndChangingPKTest.class, + com.dotcms.enterprise.publishing.remote.bundler.TemplateBundlerTest.class, + com.dotcms.rendering.velocity.viewtools.JSONToolTest.class, + com.dotmarketing.startup.runonce.Task240530AddDotAIPortletToLayoutTest.class, + com.dotcms.enterprise.publishing.remote.bundler.RuleBundlerTest.class, + com.dotcms.rest.api.v1.system.ConfigurationHelperTest.class, com.dotmarketing.startup.runonce.Task05035LanguageTableIdentityOffTest.class, - com.dotmarketing.startup.runonce.Task05165CreateContentTypeWorkflowActionMappingTableTest.class, - com.dotmarketing.startup.runonce.Task05070AndTask05080Test.class, - com.dotmarketing.startup.runonce.Task05030UpdateSystemContentTypesHostTest.class, - com.dotmarketing.startup.runonce.Task05050FileAssetContentTypeReadOnlyFileNameTest.class, - com.dotmarketing.startup.runonce.Task05190UpdateFormsWidgetCodeFieldTest.class, - com.dotmarketing.startup.runalways.Task00001LoadSchemaIntegrationTest.class, - com.dotmarketing.startup.runonce.Task05200WorkflowTaskUniqueKeyTest.class, - Task05195CreatesDestroyActionAndAssignDestroyDefaultActionsToTheSystemWorkflowTest.class, - Task05210CreateDefaultDotAssetTest.class, - DotAssetAPITest.class, - DotAssetBaseTypeToContentTypeStrategyImplTest.class, - FileAssetAPIImplIntegrationTest.class, - FileAssetFactoryIntegrationTest.class, - UserResourceIntegrationTest.class, - IntegrationResourceLinkTest.class, - HashBuilderTest.class, - LanguageUtilTest.class, - FolderResourceTest.class, - FolderResourceSearchTest.class, - Task05225RemoveLoadRecordsToIndexTest.class, - PublisherFilterImplTest.class, - PushPublishFiltersInitializerTest.class, - PushPublishFilterResourceTest.class, - PublishingResourceIntegrationTest.class, - BundleManagementResourceIntegrationTest.class, - PushNowActionletTest.class, - Task05305AddPushPublishFilterColumnTest.class, - CMSMaintenanceFactoryTest.class, - Task05350AddDotSaltClusterColumnTest.class, - PostgresPubSubImplTest.class, - DotParseTest.class, - TestWorkflowAction.class, - SamlConfigurationServiceTest.class, - ClusterFactoryTest.class, - BundleResourceTest.class, - IdentityProviderConfigurationFactoryTest.class, - GoogleTranslationServiceIntegrationTest.class, - Task240131UpdateLanguageVariableContentTypeTest.class, - PushedAssetUtilTest.class, - OpenAIAutoTagActionletTest.class, - Task250828CreateCustomAttributeTableTest.class, - CustomAttributeAPIImplTest.class, - CustomAttributeFactoryTest.class, - PermissionResourceIntegrationTest.class, - FileAssetBaseTypeToContentTypeStrategyImplTest.class, + com.dotcms.publisher.bundle.business.BundleFactoryImplTest.class, + com.dotcms.rest.api.v1.apps.view.AppsInterpolationTest.class, + com.dotcms.analytics.track.RequestMatcherTest.class, + com.dotmarketing.startup.runonce.Task260720AddDefaultBaseTypeToFolderTableTest.class, + com.dotmarketing.util.ResourceCollectorUtilTest.class, + com.dotmarketing.startup.runonce.Task250107RemoveEsReadOnlyMonitorJobTest.class, + com.dotcms.mock.request.CachedParameterDecoratorTest.class, + com.dotcms.cache.lettuce.DotObjectCodecTest.class, + com.dotcms.storage.repository.HashedLocalFileRepositoryManagerTest.class, + com.dotmarketing.startup.runonce.Task240112AddMetadataColumnToStructureTableTest.class, + com.dotmarketing.util.ConfigUtilsTest.class }) - public class MainSuite2b { + } diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite3a.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite3a.java index 740cfe36d921..85c765c83d49 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite3a.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite3a.java @@ -1,105 +1,108 @@ package com.dotcms; -import com.dotcms.ai.api.OpenAIVisionAPIImplTest; -import com.dotcms.ai.util.ContentToStringUtilTest; -import com.dotcms.contenttype.business.StoryBlockValidationTest; -import com.dotcms.contenttype.test.StoryBlockUtilTest; -import com.dotcms.cost.RequestCostReportTest; -import com.dotcms.jitsu.validators.AnalyticsValidatorUtilTest; import com.dotcms.junit.MainBaseSuite; -import com.dotcms.publisher.business.PublisherQueueJobTest; -import com.dotcms.rest.api.v1.drive.ContentDriveFieldFilterTest; -import com.dotcms.rest.api.v1.drive.ContentDriveHelperContentletAPIComparisonTest; -import com.dotcms.rest.api.v1.drive.ContentDriveKeywordSearchTest; -import com.dotcms.rest.api.v1.drive.ContentDriveWorkflowArchiveStepTest; -import com.dotcms.rest.api.v1.drive.ContentDriveWorkflowFilterTest; -import com.dotcms.security.apps.AppsAPIImplTest; -import com.dotcms.telemetry.collectors.MetricTimeoutTest; -import com.dotcms.telemetry.collectors.experiment.CountPagesWithAllEndedExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountPagesWithArchivedExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountPagesWithDraftExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountPagesWithRunningExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountPagesWithScheduledExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllArchivedExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllDraftExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllEndedExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllRunningExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllScheduledExperimentsMetricTypeTest; -import com.dotcms.telemetry.collectors.theme.TotalSizeOfFilesPerThemeMetricTypeTest; -import com.dotcms.util.TimeMachineUtilTest; -import com.dotmarketing.business.DeterministicIdentifierAPITest; -import com.dotmarketing.business.SecondaryCategoryPermissionTest; -import com.dotmarketing.factories.TreeFactoryTest; -import com.dotmarketing.fixtask.tasks.FixTask00090RecreateMissingFoldersInParentPathTest; -import com.dotmarketing.portlets.contentlet.action.ImportContentletsActionSmokeTest; -import com.dotmarketing.portlets.rules.RuleAPITest; -import com.dotmarketing.startup.runonce.Task230630CreateRunningIdsExperimentFieldIntegrationTest; -import com.dotmarketing.startup.runonce.Task250604UpdateFolderInodesTest; -import com.dotmarketing.startup.runonce.Task250826AddIndexesToUniqueFieldsTableTest; -import com.dotmarketing.startup.runonce.Task251103AddStylePropertiesColumnInMultiTreeTest; -import com.dotmarketing.startup.runonce.Task251212AddVersionColumnIndicesTableTest; -import com.dotmarketing.startup.runonce.Task260206AddUsagePortletToMenuTest; -import com.dotmarketing.startup.runonce.Task260320AddPluginsPortletToMenuTest; -import com.dotmarketing.startup.runonce.Task260407AddBaseTypeColumnToIdentifierTest; -import com.dotmarketing.startup.runonce.Task260505AddPluginsPortletToMenuTest; -import com.dotmarketing.startup.runonce.Task260615AlterClusterIdLengthTest; import org.junit.runner.RunWith; -import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +/** + * Integration test suite shard 5 of 7. + * + * Shards are balanced on measured per-class test time so the slowest shard + * bounds the CI critical path as tightly as possible. When adding a test, + * put it in the shard with the lowest total time rather than appending here + * by habit - see .github/test-matrix.yml for the shard list. + * + * Classes are fully qualified so that rebalancing does not churn imports. + */ @RunWith(MainBaseSuite.class) -@Suite.SuiteClasses({ - RuleAPITest.class, - DeterministicIdentifierAPITest.class, - CountPagesWithAllEndedExperimentsMetricTypeTest.class, - CountPagesWithArchivedExperimentsMetricTypeTest.class, - CountPagesWithDraftExperimentsMetricTypeTest.class, - CountPagesWithRunningExperimentsMetricTypeTest.class, - CountPagesWithScheduledExperimentsMetricTypeTest.class, - CountVariantsInAllArchivedExperimentsMetricTypeTest.class, - CountVariantsInAllDraftExperimentsMetricTypeTest.class, - CountVariantsInAllEndedExperimentsMetricTypeTest.class, - CountVariantsInAllRunningExperimentsMetricTypeTest.class, - CountVariantsInAllScheduledExperimentsMetricTypeTest.class, - MetricTimeoutTest.class, - Task230630CreateRunningIdsExperimentFieldIntegrationTest.class, - TotalSizeOfFilesPerThemeMetricTypeTest.class, - TimeMachineUtilTest.class, - Task250604UpdateFolderInodesTest.class, - FixTask00090RecreateMissingFoldersInParentPathTest.class, - AnalyticsValidatorUtilTest.class, - Task250826AddIndexesToUniqueFieldsTableTest.class, - SecondaryCategoryPermissionTest.class, - RequestCostReportTest.class, - OpenAIVisionAPIImplTest.class, - ContentDriveFieldFilterTest.class, - ContentDriveHelperContentletAPIComparisonTest.class, - ContentDriveKeywordSearchTest.class, - ContentDriveWorkflowArchiveStepTest.class, - ContentDriveWorkflowFilterTest.class, - AppsAPIImplTest.class, - com.dotcms.content.elasticsearch.business.ESContentletAPIImplTest.class, - com.dotcms.rendering.velocity.viewtools.content.util.ContentUtilsTest.class, - com.dotcms.browser.BrowserAPITest.class, - com.dotcms.rest.api.v1.content.search.strategies.GlobalSearchAttributeStrategyMatchingTest.class, - com.dotcms.contenttype.test.ContentResourceTest.class, - com.dotmarketing.portlets.htmlpages.business.render.HTMLPageAssetRenderedAPIImplIntegrationTest.class, - com.dotcms.contenttype.business.ContentTypeDestroyAPIImplTest.class, +@SuiteClasses({ + + // Data-scanning tests run FIRST on purpose. + // Integration tests accumulate content and never clean up, so anything + // that walks the whole dataset (executeUpgrade, findAll*) costs + // O(all content created so far). Scheduled late these pay for every + // preceding test's leftovers. Keep new full-scan tests in this block. + com.dotmarketing.portlets.containers.business.ContainerAPIImplTest.class, + com.dotcms.ema.EMAWebInterceptorTest.class, + + com.dotmarketing.portlets.contentlet.business.ContentletAPITest.class, + com.dotcms.experiments.business.ExperimentAPIImpIntegrationTest.class, + com.dotcms.util.content.json.PopulateContentletAsJSONUtilTest.class, com.dotcms.rest.api.v1.apps.AppsResourceTest.class, - Task251103AddStylePropertiesColumnInMultiTreeTest.class, - StoryBlockValidationTest.class, - StoryBlockUtilTest.class, - Task251212AddVersionColumnIndicesTableTest.class, - Task260206AddUsagePortletToMenuTest.class, - Task260320AddPluginsPortletToMenuTest.class, - Task260505AddPluginsPortletToMenuTest.class, - Task260407AddBaseTypeColumnToIdentifierTest.class, - Task260615AlterClusterIdLengthTest.class, - ImportContentletsActionSmokeTest.class, - TreeFactoryTest.class, - PublisherQueueJobTest.class, - ContentToStringUtilTest.class, + com.dotmarketing.quartz.DotStatefulJobTest.class, + com.dotmarketing.startup.runonce.Task05380ChangeContainerPathToAbsoluteTest.class, + com.dotcms.uuid.shorty.ShortyIdApiTest.class, + com.dotcms.rest.api.v1.taillog.TailLogResourceTest.class, + com.dotmarketing.portlets.containers.business.ContainerAPITest.class, + com.dotmarketing.portlets.contentlet.transform.ContentletTransformerTest.class, + com.dotmarketing.factories.PublishFactoryTest.class, + com.dotmarketing.portlets.structure.factories.StructureFactoryTest.class, + com.dotcms.rest.BundleResourceTest.class, + com.dotmarketing.servlets.BinaryExporterServletTest.class, + com.dotcms.enterprise.publishing.bundler.URLMapBundlerTest.class, + com.dotcms.rest.api.v1.vtl.VTLResourceIntegrationTest.class, + com.dotcms.filters.VanityUrlFilterTest.class, + com.dotmarketing.portlets.fileassets.business.FileAssetFactoryIntegrationTest.class, + com.dotcms.integritycheckers.IntegrityUtilTest.class, + com.dotcms.concurrent.lock.DotKeyLockManagerTest.class, + com.dotcms.rest.StoryBlockMarkdownPopulatorTest.class, + com.dotmarketing.portlets.workflows.actionlet.FourEyeApproverActionletTest.class, + com.dotmarketing.business.DeterministicIdentifierAPITest.class, + com.dotmarketing.portlets.folders.business.FolderFactoryImplTest.class, + com.dotcms.translate.GoogleTranslationServiceIntegrationTest.class, + com.dotcms.util.pagination.ContentTypesPaginatorTest.class, + com.dotcms.rest.api.v1.drive.ContentDriveKeywordSearchTest.class, + com.dotcms.telemetry.collectors.experiment.CountVariantsInAllArchivedExperimentsMetricTypeTest.class, + com.dotcms.telemetry.collectors.experiment.CountPagesWithScheduledExperimentsMetricTypeTest.class, + com.dotcms.content.elasticsearch.business.ESIndexSpeedTest.class, + com.dotcms.telemetry.collectors.experiment.CountPagesWithAllEndedExperimentsMetricTypeTest.class, + com.dotcms.content.model.hydration.MetadataDelegateTest.class, + com.dotcms.rendering.velocity.viewtools.content.StoryBlockMapTest.class, + com.dotcms.graphql.datafetcher.CategoryFieldDataFetcherTest.class, + com.dotmarketing.portlets.workflows.actionlet.SaveContentAsDraftActionletIntegrationTest.class, + com.dotcms.ai.workflow.OpenAIAutoTagActionletTest.class, + com.dotcms.csspreproc.CSSPreProcessServletIT.class, + com.dotmarketing.portlets.links.factories.LinkFactoryTest.class, + com.dotmarketing.portlets.links.business.MenuLinkAPITest.class, + com.dotmarketing.sitesearch.viewtool.SiteSearchWebAPITest.class, + com.dotcms.enterprise.publishing.remote.handler.ContentHandlerTest.class, + com.liferay.portal.ejb.UserLocalManagerTest.class, + com.dotmarketing.startup.runonce.Task05190UpdateFormsWidgetCodeFieldTest.class, + com.dotcms.contenttype.test.ContentTypeTest.class, + com.dotcms.rendering.velocity.viewtools.LanguageWebAPITest.class, + com.dotcms.auth.providers.saml.v1.DotSamlResourceTest.class, + com.dotcms.rest.api.v1.announcements.RemoteAnnouncementsLoaderIntegrationTest.class, + com.dotcms.contenttype.model.field.layout.FieldLayoutSerializerTest.class, + com.liferay.util.LocaleUtilTest.class, + com.dotcms.publisher.receiver.BundlePublisherTest.class, + com.dotcms.enterprise.publishing.remote.bundler.HostBundlerTest.class, + com.dotcms.security.apps.SecretsStoreKeyStoreImplTest.class, + com.dotcms.analytics.metrics.QueryParameterValuesTransformerTest.class, + com.dotmarketing.portlets.rules.RuleAPITest.class, + com.dotmarketing.startup.runonce.Task250113CreatePostgresJobQueueTablesTest.class, + com.dotcms.enterprise.publishing.remote.handler.HandlerUtilTest.class, + com.dotmarketing.startup.runonce.Task221007AddVariantIntoPrimaryKeyTest.class, + com.dotcms.cache.lettuce.LettuceCacheTest.class, + com.dotmarketing.startup.runonce.Task220203RemoveFolderInodeConstraintTest.class, + com.dotmarketing.startup.runonce.Task05165CreateContentTypeWorkflowActionMappingTableTest.class, + com.dotmarketing.startup.runonce.Task05070AndTask05080Test.class, + com.dotmarketing.startup.runonce.Task240131UpdateLanguageVariableContentTypeTest.class, + com.dotmarketing.startup.runonce.Task201013AddNewColumnsToIdentifierTableTest.class, + com.dotmarketing.startup.runonce.Task04375UpdateCategoryKeyTest.class, + com.dotmarketing.startup.runonce.Task260206AddUsagePortletToMenuTest.class, + com.dotmarketing.portlets.rules.conditionlet.UsersBrowserLanguageConditionletTest.class, + com.dotmarketing.startup.runonce.Task230713IncreaseDisabledWysiwygColumnSizeTest.class, + com.dotcms.rest.api.v3.contenttype.MoveFieldFormTest.class, + com.dotmarketing.startup.runonce.Task220401CreateClusterLockTableTest.class, + com.dotcms.variant.business.VariantCacheTest.class, + com.dotmarketing.startup.runonce.Task05050FileAssetContentTypeReadOnlyFileNameTest.class, + com.dotcms.cdi.SimpleInjectionIT.class, + com.dotmarketing.portlets.rules.conditionlet.UsersSiteVisitsConditionletTest.class, + com.dotcms.cdi.SimpleJUnit4InjectionIT.class, + com.dotmarketing.util.TestConfig.class, + com.dotmarketing.startup.runonce.Task05390MakeRoomForLongerJobDetailTest.class, + com.dotmarketing.util.HashBuilderTest.class }) - public class MainSuite3a { } diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite3b.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite3b.java new file mode 100644 index 000000000000..7a16e3736922 --- /dev/null +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite3b.java @@ -0,0 +1,108 @@ +package com.dotcms; + +import com.dotcms.junit.MainBaseSuite; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Integration test suite shard 6 of 7. + * + * Shards are balanced on measured per-class test time so the slowest shard + * bounds the CI critical path as tightly as possible. When adding a test, + * put it in the shard with the lowest total time rather than appending here + * by habit - see .github/test-matrix.yml for the shard list. + * + * Classes are fully qualified so that rebalancing does not churn imports. + */ +@RunWith(MainBaseSuite.class) +@SuiteClasses({ + + // Data-scanning tests run FIRST on purpose. + // Integration tests accumulate content and never clean up, so anything + // that walks the whole dataset (executeUpgrade, findAll*) costs + // O(all content created so far). Scheduled late these pay for every + // preceding test's leftovers. Keep new full-scan tests in this block. + com.dotmarketing.portlets.templates.business.TemplateAPITest.class, + + com.dotcms.content.elasticsearch.business.ESContentletAPIImplTest.class, + com.dotcms.rendering.velocity.services.HTMLPageAssetRenderedTest.class, + com.dotcms.content.elasticsearch.business.ESIndexAPITest.class, + com.dotmarketing.portlets.folders.business.FolderAPITest.class, + com.dotmarketing.cms.urlmap.URLMapAPIImplTest.class, + com.dotcms.storage.FileMetadataAPITest.class, + com.dotmarketing.portlets.htmlpages.business.HTMLPageAPITest.class, + com.dotmarketing.portlets.languagesmanager.business.LanguageAPITest.class, + com.dotmarketing.portlets.containers.business.FileAssetContainerUtilTest.class, + com.dotmarketing.portlets.browser.BrowserUtilTest.class, + com.dotmarketing.business.VersionableAPITest.class, + com.dotcms.content.business.json.ContentletJsonAPITest.class, + com.dotcms.enterprise.priv.ESSearchProxyTest.class, + com.dotcms.enterprise.publishing.PublishDateUpdaterIntegrationTest.class, + com.dotmarketing.business.PermissionAPIIntegrationTest.class, + com.dotcms.rest.api.v2.asset.WebAssetResourceV2IntegrationTest.class, + com.dotcms.rest.api.v1.system.permission.PermissionResourceIntegrationTest.class, + com.dotmarketing.business.VersionableFactoryImplTest.class, + com.dotcms.rest.api.v1.contenttype.ContentTypeResourceTest.class, + com.dotmarketing.portlets.contentlet.business.ContentletFactoryTest.class, + com.dotmarketing.portlets.templates.business.FileAssetTemplateUtilTest.class, + com.dotmarketing.filters.CMSUrlUtilIntegrationTest.class, + com.dotcms.rest.api.v3.contenttype.FieldResourceTest.class, + com.dotcms.rest.api.v1.contenttype.ContentTypeResourceIssue15124Test.class, + com.dotcms.graphql.datafetcher.page.NumberContentsDataFetcherTest.class, + com.dotcms.analytics.track.collectors.PagesCollectorTest.class, + com.dotcms.contenttype.test.DeleteFieldJobTest.class, + com.dotcms.contenttype.test.JsonContentTypeTransformerTest.class, + com.dotcms.telemetry.collectors.experiment.CountPagesWithRunningExperimentsMetricTypeTest.class, + com.dotcms.telemetry.collectors.experiment.CountVariantsInAllScheduledExperimentsMetricTypeTest.class, + com.dotcms.contenttype.business.SiteAndFolderResolverImplTest.class, + com.dotmarketing.portlets.workflows.business.SaveContentActionletTest.class, + com.dotcms.ai.viewtool.EmbeddingsToolTest.class, + com.dotcms.ai.app.ConfigServiceTest.class, + com.dotcms.rendering.velocity.viewtools.WebsiteToolTest.class, + com.dotmarketing.osgi.GenericBundleActivatorIntegrationTest.class, + com.dotmarketing.portlets.containers.business.ContainerStructureFinderStrategyResolverTest.class, + com.dotcms.publisher.bundle.business.BundleAPITest.class, + com.dotcms.publisher.bundle.business.BundleFactoryTest.class, + com.dotmarketing.factories.WebAssetFactoryTest.class, + com.dotmarketing.portlets.containers.business.ContainerFactoryImplTest.class, + com.dotcms.rest.api.v1.container.ContainerResourceIntegrationTest.class, + com.dotcms.auth.providers.jwt.JsonWebTokenUtilsIntegrationTest.class, + com.dotcms.ai.viewtool.CompletionsToolTest.class, + com.dotcms.enterprise.publishing.remote.handler.RuleBundlerHandlerTest.class, + com.dotmarketing.portlets.workflows.business.WorkflowFactoryTest.class, + com.dotcms.contenttype.model.field.layout.FieldLayoutTest.class, + com.dotcms.rest.api.v1.asset.AssetPathResolverImplIntegrationTest.class, + com.dotmarketing.portlets.rules.conditionlet.VisitedUrlConditionletTest.class, + com.dotcms.analytics.attributes.CustomAttributeAPIImplTest.class, + com.dotcms.publisher.endpoint.business.PublishingEndPointAPITest.class, + com.dotmarketing.portlets.workflows.model.SystemActionWorkflowActionMappingTest.class, + com.dotcms.publishing.PushPublishFiltersInitializerTest.class, + com.dotcms.rendering.velocity.services.VelocityResourceKeyTest.class, + com.dotcms.cluster.business.ServerAPIImplTest.class, + com.dotcms.enterprise.publishing.remote.bundler.ContentTypeBundlerTest.class, + com.dotmarketing.common.db.DBTimeZoneCheckTest.class, + com.dotmarketing.factories.TreeFactoryTest.class, + com.dotmarketing.startup.runonce.Task220202RemoveFKStructureFolderConstraintTest.class, + com.dotmarketing.startup.runonce.Task05395RemoveEndpointIdForeignKeyInIntegrityResolverTablesIntegrationTest.class, + com.dotmarketing.startup.runonce.Task220215MigrateDataFromInodeToFolderTest.class, + com.dotcms.mail.MailAPIImplTest.class, + com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionletAbortTest.class, + com.dotmarketing.startup.runonce.Task230701AddHashIndicesToWorkflowTablesTest.class, + com.dotmarketing.startup.runonce.Task220413IncreasePublishedPushedAssetIdColTest.class, + com.dotmarketing.startup.runonce.Task220912UpdateCorrectShowOnMenuPropertyTest.class, + com.dotmarketing.startup.runonce.Task250828CreateCustomAttributeTableTest.class, + com.dotcms.util.marshal.MarshalUtilsIntegrationTest.class, + com.dotmarketing.startup.runonce.Task220402UpdateDateTimezonesTest.class, + com.dotmarketing.startup.runonce.Task05350AddDotSaltClusterColumnTest.class, + com.dotcms.rest.AuditPublishingResourceTest.class, + com.dotmarketing.startup.runonce.Task241015ReplaceLanguagesWithLocalesPortletTest.class, + com.dotmarketing.startup.runonce.Task210805DropUserProxyTableTest.class, + com.dotmarketing.beans.HostTest.class, + com.dotmarketing.startup.runonce.Task211007RemoveNotNullConstraintFromCompanyMXColumnTest.class, + com.dotmarketing.common.db.DotConnectTest.class, + com.dotmarketing.startup.runonce.Task04375UpdateColorsTest.class, + com.dotmarketing.db.DbConnectionFactoryUtilTest.class +}) +public class MainSuite3b { + +} diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite4a.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite4a.java new file mode 100644 index 000000000000..2c3ebfe31712 --- /dev/null +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite4a.java @@ -0,0 +1,108 @@ +package com.dotcms; + +import com.dotcms.junit.MainBaseSuite; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Integration test suite shard 7 of 7. + * + * Shards are balanced on measured per-class test time so the slowest shard + * bounds the CI critical path as tightly as possible. When adding a test, + * put it in the shard with the lowest total time rather than appending here + * by habit - see .github/test-matrix.yml for the shard list. + * + * Classes are fully qualified so that rebalancing does not churn imports. + */ +@RunWith(MainBaseSuite.class) +@SuiteClasses({ + + // Data-scanning tests run FIRST on purpose. + // Integration tests accumulate content and never clean up, so anything + // that walks the whole dataset (executeUpgrade, findAll*) costs + // O(all content created so far). Scheduled late these pay for every + // preceding test's leftovers. Keep new full-scan tests in this block. + com.dotcms.content.elasticsearch.util.ESMappingUtilHelperTest.class, + + com.dotcms.contenttype.test.ContentTypeAPIImplTest.class, + com.dotmarketing.portlets.htmlpages.business.render.HTMLPageAssetRenderedAPIImplIntegrationTest.class, + com.dotcms.contenttype.test.ContentTypeFactoryImplTest.class, + com.dotcms.publisher.util.DependencyModDateUtilTest.class, + com.dotmarketing.portlets.workflows.business.WorkflowAPITest.class, + com.dotmarketing.tag.business.TagAPITest.class, + com.dotcms.rest.api.v1.workflow.WorkflowResourceIntegrationTest.class, + com.dotcms.rendering.velocity.viewtools.navigation.NavToolTest.class, + com.dotcms.rest.api.v1.contenttype.FieldResourceTest.class, + com.dotcms.variant.VariantAPITest.class, + com.dotcms.rendering.velocity.servlet.VelocityServletIntegrationTest.class, + com.dotcms.jobs.business.api.JobQueueManagerAPITest.class, + com.dotcms.contenttype.business.StoryBlockValidationTest.class, + com.dotcms.dotpubsub.PostgresPubSubImplTest.class, + com.dotmarketing.portlets.categories.business.CategoryAPITest.class, + com.dotcms.telemetry.collectors.MetricTimeoutTest.class, + com.dotcms.rest.api.v1.publishing.BundleManagementResourceIntegrationTest.class, + com.dotmarketing.business.IdentifierFactoryTest.class, + com.dotcms.rendering.velocity.directive.DotParseTest.class, + com.dotmarketing.portlets.contentlet.ajax.ContentletAjaxTest.class, + com.dotcms.ai.listener.EmbeddingContentListenerTest.class, + com.dotcms.rest.api.v1.content.ContentVersionResourceIntegrationTest.class, + com.dotcms.rest.api.v1.authentication.ResetPasswordResourceIntegrationTest.class, + com.dotcms.rest.TagResourceIntegrationTest.class, + com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionletTest.class, + com.dotmarketing.portlets.categories.business.CategoryFactoryTest.class, + com.dotcms.rest.api.v1.page.NavResourceTest.class, + com.dotcms.publishing.PublisherAPITest.class, + com.dotcms.telemetry.collectors.experiment.CountPagesWithArchivedExperimentsMetricTypeTest.class, + com.dotcms.publisher.business.PublisherQueueJobTest.class, + com.dotmarketing.portlets.workflows.actionlet.PushNowActionletTest.class, + com.dotmarketing.portlets.workflows.business.SaveContentDraftActionletTest.class, + com.dotcms.rest.api.v1.apps.SiteViewPaginatorIntegrationTest.class, + com.dotcms.contenttype.business.ContentTypeInitializerTest.class, + com.dotcms.rest.api.v1.user.UserResourceIntegrationTest.class, + com.dotmarketing.business.SecondaryCategoryPermissionTest.class, + com.dotcms.storage.FileStorageAPITest.class, + com.dotcms.publisher.util.PushedAssetUtilTest.class, + com.dotcms.rest.api.v1.workflow.WorkflowResourceLicenseIntegrationTest.class, + com.dotcms.graphql.datafetcher.page.RunningExperimentFetcherTest.class, + com.dotcms.contenttype.model.field.layout.FieldUtilTest.class, + com.dotmarketing.util.MaintenanceUtilTest.class, + com.dotmarketing.portlets.contentlet.business.ContentletCacheImplTest.class, + com.dotmarketing.startup.runonce.Task230523CreateVariantFieldInContentletIntegrationTest.class, + com.dotmarketing.fixtask.tasks.FixTask00090RecreateMissingFoldersInParentPathTest.class, + com.dotcms.rest.api.v1.authentication.CreateJsonWebTokenResourceIntegrationTest.class, + com.dotcms.content.business.json.LegacyJSONObjectRenderTest.class, + com.dotcms.visitor.filter.characteristics.VisitorCharacterTest.class, + com.dotmarketing.fixTasks.FixTask00085FixEmptyParentPathOnIdentifierTest.class, + com.dotcms.rendering.velocity.viewtools.content.StoryBlockTest.class, + com.dotmarketing.business.web.UserWebAPIImplTest.class, + com.dotcms.auth.providers.jwt.services.JsonWebTokenServiceIntegrationTest.class, + com.dotmarketing.startup.runonce.Task260407AddBaseTypeColumnToIdentifierTest.class, + com.dotmarketing.quartz.QuartzUtilsTest.class, + com.dotcms.analytics.bayesian.BayesianAPIImplIT.class, + com.dotcms.rest.api.v1.configuration.ConfigurationResourceTest.class, + com.dotcms.enterprise.publishing.remote.bundler.FolderBundlerTest.class, + com.dotmarketing.portlets.personas.business.DeleteMultiTreeUsedPersonaTagJobTest.class, + com.dotmarketing.util.UtilMethodsITest.class, + com.dotmarketing.startup.runonce.Task230110MakeSomeSystemFieldsRemovableByBaseTypeTest.class, + com.dotmarketing.startup.runonce.Task201102UpdateColumnSitelicTableTest.class, + com.dotmarketing.startup.runonce.Task220822CreateVariantTableTest.class, + com.dotmarketing.startup.runonce.Task231109AddPublishDateToContentletVersionInfoTest.class, + com.dotcms.contenttype.test.StoryBlockUtilTest.class, + com.dotmarketing.startup.runonce.Task260505AddPluginsPortletToMenuTest.class, + com.dotmarketing.startup.runonce.Task230328AddMarkedForDeletionColumnTest.class, + com.dotmarketing.startup.runonce.Task260320AddPluginsPortletToMenuTest.class, + com.dotcms.filters.interceptor.meta.MetaWebInterceptorTest.class, + com.dotmarketing.startup.runonce.Task220606UpdatePushNowActionletNameTest.class, + com.dotmarketing.portlets.workflows.model.TestWorkflowAction.class, + com.dotmarketing.startup.runonce.Task211103RenameHostNameLabelTest.class, + com.dotmarketing.startup.runonce.Task241016AddCustomLanguageVariablesPortletToLayoutTest.class, + com.dotmarketing.startup.runonce.Task240111AddInodeAndIdentifierLeftIndexesTest.class, + com.dotmarketing.startup.runonce.Task210719CleanUpTitleFieldTest.class, + com.dotcms.enterprise.publishing.staticpublishing.LanguageFolderTest.class, + com.dotcms.security.multipart.SecureFileValidatorTest.class, + com.dotcms.publishing.PublisherFilterImplTest.class, + com.dotcms.enterprise.cluster.ClusterFactoryTest.class +}) +public class MainSuite4a { + +} diff --git a/dotcms-postman/config.json b/dotcms-postman/config.json index 024a7bc6beb7..6d9c1dd3340c 100644 --- a/dotcms-postman/config.json +++ b/dotcms-postman/config.json @@ -1,87 +1,157 @@ [ { - "name": "ai", - "collections": ["AI.postman_collection"] - }, - { - "name": "category-content", + "name": "content", "collections": [ - "Category.postman_collection", - "ContentResourceV1.postman_collection", - "Content_Resource.postman_collection" + "Content_Resource.postman_collection", + "Containers.postman_collection", + "User_Include_Into_Experiment.postman_collection", + "Bundle_Resource.postman_collection", + "BringBack.postman_collection", + "WebDav.postman_collection", + "LangVariables.postman_collection", + "Image.postman_collection", + "PortletResource", + "Maintenance_Resource-Donwload_Log_File", + "Osgi.postman_collection", + "TempAPI.postman_collection" ] }, { - "name": "container", + "name": "contenttype", "collections": [ - "ContainerResource.postman_collection", - "Containers.postman_collection" + "ContentTypeResourceTests", + "Integrity_Checker_From_Sender.postman_collection", + "DotAsset.postman_collection", + "Visitor.postman_collection", + "Push_Publish_JWT_Token_Test.postman_collection", + "JobQueueResourceAPITests.postman_collection", + "CacheResource.postman_collection", + "UIComponents.postman_collection", + "PublishingResource.postman_collection", + "MonitorResource.postman_collection", + "ForgotPasswordResource.postman_collection", + "DateTool.postman_collection", + "Maintenance_Resource", + "ProbesResource.postman_collection" ] }, { - "name": "experiment", + "name": "graphql-a", "collections": [ - "Experiments_Resource.postman_collection", - "Experiment_Result.postman_collection" - ] + "GraphQLTests", + "DotFavoritePage.postman_collection", + "JsScriptAPI.postman_collection", + "SystemTable.postman_collection", + "Scripting_Resource.postman_collection", + "PushPublishFilterResource.postman_collection", + "Apps.postman_collection", + "EMA.postman_collection" + ], + "folders": { + "GraphQLTests": [ + "Pre-Execution Requests", + "Page API" + ] + } }, { - "name": "graphql", - "collections": ["GraphQLTests"] + "name": "graphql-b", + "collections": [ + "GraphQLTests", + "LanguageResourceTests", + "ContainerResource.postman_collection", + "Field_Variable_Resource.postman_collection", + "Content_Version_Resource.postman_collection", + "ContentTypePages.postman_collection", + "VelocityMacro.postman_collection", + "Content_Analytics.postman_collection", + "Maintenance_Resource-Download_Starter", + "Promote_Variant.postman_collection", + "ThemeResource.postman_collection" + ], + "folders": { + "GraphQLTests": [ + "Pre-Execution Requests", + "Query Cache", + "Test BaseType fields ", + "Related content with condition / query", + "PageAPI_TestMapSpecialField", + "Test File/Image Field fields", + "Nav", + "Related content respects language in query for parent", + "Tests For New StoryBlockField", + "Get content in new Language", + "Pagination", + "Metadata ", + "Page Lock Test", + "Test Render Content Fields", + "Test DateField Render Right Format", + "Given JSONField should return as JSON", + "Cats", + "File Metadata", + "Tags", + "Empty Collection", + "Disallow Introspection Query", + "Page API - Testing 'page' field with inline fragments", + "DotFolderByPath Tests" + ] + } }, { - "name": "page", - "collections": ["PagesResourceTests"] + "name": "pages", + "collections": [ + "PagesResourceTests", + "Define_Contentlets_StyleProperties.postman_collection", + "VersionableResource.postman_collection", + "Permission_Resource.postman_collection", + "Tags_Resource_V2.postman_collection", + "Browser_Resource.postman_collection", + "Reltionship_cache_in_push_publish.postman_collection", + "PPEndpointResource.postman_collection", + "DotAsset-MultiPart-TempFile.postman_collection", + "PublishQueueResource" + ] }, { - "name": "pp", + "name": "site", "collections": [ - "PublishQueueResource", + "Site_Resource.postman_collection", + "ContentResourceV1.postman_collection", + "Category.postman_collection", + "ContentDriveResource.postman_collection", "Push_Publish_from_sender.postman_collection", - "Push_Publish_JWT_Token_Test.postman_collection", - "PushPublishFilterResource.postman_collection" + "AI.postman_collection", + "VanityURL.postman_collection", + "Announcements.postman_collection", + "VelocitySecrets.postman_collection", + "ContentImportResource.postman_collection", + "Logger_Resource.postman_collection", + "Accessibility_Checker_Tests.postman_collection" ] }, { "name": "template", "collections": [ "Template_Resource.postman_collection", - "ContentTypeResourceTests" + "ApiToken_Resource.postman_collection", + "NavResourceTests", + "UserResource.postman_collection", + "Integrity_Checker_JWT_Token_Test.postman_collection", + "Save_Layout_With_Relative_Path.postman_collection", + "Content_Report_Resource.postman_collection", + "EnvironmentResource.postman_collection", + "Form_Resource.postman_collection", + "Experiment_Result.postman_collection", + "RoleResource.postman_collection" ] }, { "name": "workflow", - "collections": ["Workflow_Resource_Tests"] - }, - { - "name": "default-split", "collections": [ - "ApiToken_Resource.postman_collection", - "ContentImportResource.postman_collection", - "Manifest_Download_End_Point.postman_collection", - "Osgi.postman_collection", - "Page_Version_with_different_Templates.postman_collection", - "Permission_Resource.postman_collection", - "Promote_Variant.postman_collection", - "Relationship_cache_in_push_publish.postman_collection", + "Workflow_Resource_Tests", + "ConfigurationResource.postman_collection", "ResourceLink.postman_collection", - "RoleResource.postman_collection", - "Scripting_Resource.postman_collection", - "Site_Resource.postman_collection", - "System.postman_collection", - "SystemTable.postman_collection", - "Tags_Resource_V2.postman_collection", - "TempAPI.postman_collection", - "ThemeResource.postman_collection", - "ToolGroupResource.postman_collection", - "UIComponents.postman_collection", - "UserResource.postman_collection", - "User_Include_Into_Experiment.postman_collection", - "VelocityMacro.postman_collection", - "VelocitySecrets.postman_collection", - "VersionableResource.postman_collection", - "Visitor.postman_collection", - "WebAssets.postman_collection" + "System.postman_collection" ] } ] diff --git a/dotcms-postman/index.js b/dotcms-postman/index.js index a084896f2dff..e98622147dd8 100644 --- a/dotcms-postman/index.js +++ b/dotcms-postman/index.js @@ -145,7 +145,8 @@ async function runNewman( collectionName, postmanTestsDir, postmanTestsResultsDir, - jwt + jwt, + folders ) { return new Promise((resolve, reject) => { const collectionPath = path.join(postmanTestsDir, `${collectionName}.json`); @@ -156,6 +157,9 @@ async function runNewman( console.log("Running collection:", collectionName); console.log("using jwt:", jwt); + if (folders && folders.length) { + console.log("restricted to folders:", folders.join(", ")); + } // Validate and sanitize environment variables const envVars = [ @@ -166,6 +170,11 @@ async function runNewman( // Add additional configuration for Node.js 22 compatibility const newmanConfig = { collection: require(collectionPath), + // When a group pins `folders`, run only those top-level folders. Lets a + // single expensive collection be sharded across CI jobs without splitting + // the collection file. Newman preserves collection order, so a shared + // setup folder listed here still runs before the rest. + ...(folders && folders.length ? { folder: folders } : {}), envVar: envVars, reporters: ["junit", "cli"], reporter: { @@ -282,6 +291,8 @@ async function processCollections( console.log(`Starting collections for groupname: ${groupname}`); let collectionsToRun = []; + // Optional per-collection folder restriction, keyed by collection name. + let folderMap = {}; const collectionFile = path.join(postmanTestsDir, groupname + ".json"); if (fs.existsSync(collectionFile)) { @@ -305,12 +316,36 @@ async function processCollections( const configItem = config.find((item) => item.name === groupname); if (configItem) { collectionsToRun = configItem.collections; + folderMap = configItem.folders || {}; } else { console.error(`Collection or groupname '${groupname}' not found.`); process.exit(1); } } + // Validate pinned folders up front. A folder name that does not exist makes + // newman run zero requests and still exit green, so a typo would silently + // delete test coverage. Treated as a config error like an unknown groupname: + // fail immediately rather than let the run report success. + for (const [collection, folders] of Object.entries(folderMap)) { + if (!collectionsToRun.includes(collection)) { + console.error( + `Group '${groupname}' pins folders for '${collection}', which it does not run.` + ); + process.exit(1); + } + const doc = require(path.join(postmanTestsDir, `${collection}.json`)); + const known = new Set((doc.item || []).filter((i) => i.item).map((i) => i.name)); + const unknown = folders.filter((f) => !known.has(f)); + if (unknown.length) { + console.error( + `Collection '${collection}' has no folder(s): ${unknown.join(", ")}\n` + + `Known folders: ${[...known].join(" | ")}` + ); + process.exit(1); + } + } + // Run Newman for each collection and track failures for (let collection of collectionsToRun) { try { @@ -319,7 +354,8 @@ async function processCollections( collection, postmanTestsDir, postmanTestsResultsDir, - jwt + jwt, + folderMap[collection] ); console.log(`Collection ${collection} executed successfully.`); } catch (error) { diff --git a/dotcms-postman/verify-config.js b/dotcms-postman/verify-config.js new file mode 100644 index 000000000000..bf21b310e577 --- /dev/null +++ b/dotcms-postman/verify-config.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node +/** + * Sanity-checks dotcms-postman/config.json against what is actually on disk. + * + * Catches the failure modes that are otherwise silent in CI: + * - a collection listed in a group that has no .json file (group errors out) + * - a `folders` entry naming a folder that does not exist in the collection + * (newman runs ZERO requests and still exits green) + * - a collection claimed by two groups (runs twice, wastes a shard) + * - `folders` shards of one collection that do not cover every folder + * + * Run: node dotcms-postman/verify-config.js + */ +const fs = require("fs"); +const path = require("path"); + +const DIR = path.join(__dirname, "src/main/resources/postman"); +const config = JSON.parse(fs.readFileSync(path.join(__dirname, "config.json"))); + +const onDisk = new Set( + fs.readdirSync(DIR) + .filter((f) => f.endsWith(".json") && f !== "postman_environment.json") + .map((f) => f.replace(/\.json$/, "")) +); + +const errors = []; +const owner = new Map(); // collection -> [group, ...] +const folderShards = new Map(); // collection -> [[folders], ...] + +for (const group of config) { + if (!group.name) errors.push("a group is missing `name`"); + for (const coll of group.collections || []) { + if (!onDisk.has(coll)) { + errors.push(`[${group.name}] collection not on disk: ${coll}`); + continue; + } + if (!owner.has(coll)) owner.set(coll, []); + owner.get(coll).push(group.name); + + const folders = (group.folders || {})[coll]; + if (!folders) continue; + + const doc = JSON.parse(fs.readFileSync(path.join(DIR, `${coll}.json`))); + const real = new Set(doc.item.filter((i) => i.item).map((i) => i.name)); + for (const f of folders) { + if (!real.has(f)) { + errors.push( + `[${group.name}] ${coll}: folder "${f}" does not exist ` + + `(newman would run zero requests). Known: ${[...real].join(" | ")}` + ); + } + } + if (!folderShards.has(coll)) folderShards.set(coll, { real, shards: [] }); + folderShards.get(coll).shards.push(folders); + } +} + +// A collection may legitimately appear in >1 group ONLY when each appearance +// pins a disjoint set of folders. +for (const [coll, groups] of owner) { + if (groups.length === 1) continue; + const entry = folderShards.get(coll); + if (!entry || entry.shards.length !== groups.length) { + errors.push(`${coll} is claimed by ${groups.join(", ")} without folder pinning`); + continue; + } + const counts = new Map(); + for (const s of entry.shards) { + for (const f of s) counts.set(f, (counts.get(f) || 0) + 1); + } + // The shared setup folder is expected in every shard; anything else repeated + // means real duplicated work. + const dupes = [...counts].filter(([f, n]) => n > 1 && n !== entry.shards.length); + if (dupes.length) { + errors.push(`${coll}: folders in some-but-not-all shards: ${dupes.map(([f]) => f).join(", ")}`); + } + const missing = [...entry.real].filter((f) => !counts.has(f)); + if (missing.length) { + errors.push(`${coll}: folders covered by NO shard: ${missing.join(", ")}`); + } +} + +const listed = new Set(owner.keys()); +const fallsToDefault = [...onDisk].filter((c) => !listed.has(c)); + +console.log(`collections on disk : ${onDisk.size}`); +console.log(`explicitly grouped : ${listed.size} across ${config.length} groups`); +console.log(`-> "default" shard : ${fallsToDefault.length}`); +for (const [coll, e] of folderShards) { + console.log(`folder-sharded : ${coll} -> ${e.shards.length} shards covering ${e.real.size} folders`); +} + +if (errors.length) { + console.error(`\n${errors.length} PROBLEM(S):`); + errors.forEach((e) => console.error(" - " + e)); + process.exit(1); +} +console.log("\nconfig.json OK"); From 26b83911ea470bdc9b0fdb03364464d677d030a4 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 7 Aug 2026 10:23:36 -0400 Subject: [PATCH 05/11] perf(test): batch Task240306 teardown, replace ImportUtilTest sleeps with awaitility Two of the slowest integration tests, both spending their time waiting rather than testing. Task240306MigrateLegacyLanguageVariablesTest -- 7.26m, the single most expensive IT class (5.6% of all integration test time). The cost is not executeUpgrade: testDataTaskIdempotency runs the upgrade twice and costs the same as testExecuteUpgrade running it once. It is the teardown. cleanup() walked the migration summary and did a find + destroy per inode, and a run produces well over a hundred Language Variables (en=38, fr-fr=23, es-es=34, plus others). destroy() is @WrapInTransaction, so that was ~150 transactions, each with its own ES delete and cache invalidation, three times over. Now batched through findContentlets(List) and destroy(List) -- one query and one transaction. Teardown must not leave content behind for the next test, so a batch failure falls back to the original per-contentlet loop, which tolerates individual failures. removeExistingLanguageVariables() got the same treatment. No test semantics change; only how the fixtures are torn down. ImportUtilTest -- 34s of literal Thread.sleep, the most in the suite: 30s polling for content to appear in the index. It slept 30s BEFORE the first check, so it always paid the full cost even when the index was already caught up, and its retry bound (100 iterations) allowed a 50-minute worst case. 2s waiting for an async import to land three contentlets 1s x2 waiting after addContentToIndex for content to become searchable All four replaced with awaitility, following the pattern already used in SiteSearchJobImplTest and other ITs: poll every 200ms, fail at 30s. The waits now cost what they actually need rather than a fixed guess, and a genuinely stuck index fails fast with a clear timeout instead of hanging. Also leaves a note-worthy bug untouched, deliberately: removeLanguageAndContent returns false immediately after destroying its first contentlet, so it never reaches deleteLanguage. It is used by one test whose assertions depend on the current return value, so fixing it is a behaviour change and belongs in its own PR. Not run locally (needs the full IT stack); verified by test-compile and by the integration suites in this PR. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NH4Pes5S9d1AQorJBNeFJU --- .../java/com/dotcms/util/ImportUtilTest.java | 72 ++++++++++++++----- ...306MigrateLegacyLanguageVariablesTest.java | 57 ++++++++++++--- 2 files changed, 104 insertions(+), 25 deletions(-) diff --git a/dotcms-integration/src/test/java/com/dotcms/util/ImportUtilTest.java b/dotcms-integration/src/test/java/com/dotcms/util/ImportUtilTest.java index 7c23ad95ad16..b460f6a0d39a 100644 --- a/dotcms-integration/src/test/java/com/dotcms/util/ImportUtilTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/util/ImportUtilTest.java @@ -123,6 +123,9 @@ import java.util.Map; import java.util.Set; import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static org.awaitility.Awaitility.await; import java.util.stream.Collectors; import static com.dotcms.util.CollectionsUtils.list; @@ -517,19 +520,17 @@ public void importFile() //------------------------------------------------------------------------ //Making sure the contentlets are in the indexes - List contentletSearchResults; - int x = 0; - do { - Thread.sleep(30000); - //Verify if it was added to the index - contentletSearchResults = contentletAPI.searchIndex( - "+structureName:" + contentType.getVelocityVarName() - + " +working:true +deleted:false +" + contentType - .getVelocityVarName() + ".title:Test1 +languageId:1", 0, -1, null, - user, true); - x++; - } while ((contentletSearchResults == null || contentletSearchResults.isEmpty()) - && x < 100); + final String indexedTitleQuery = + "+structureName:" + contentType.getVelocityVarName() + + " +working:true +deleted:false +" + contentType + .getVelocityVarName() + ".title:Test1 +languageId:1"; + await().atMost(30, TimeUnit.SECONDS) + .pollInterval(200, TimeUnit.MILLISECONDS) + .until(() -> { + final List indexed = contentletAPI.searchIndex( + indexedTitleQuery, 0, -1, null, user, true); + return indexed != null && !indexed.isEmpty(); + }); //Create the csv file to import reader = createTempFile(textFieldVarName + ", " + siteFieldVarName + "\r\n" + @@ -1458,8 +1459,8 @@ public void importFile_success_when_importLinesUpdateExistingContent() APILocator.getContentletIndexAPI().addContentToIndex(savedData); - //Update ContentType - Thread.sleep(1000); + //Update ContentType - wait for the content just pushed to be searchable + awaitIndexed(savedData); tempFile = "Identifier," + TITLE_FIELD_NAME + ", " + BODY_FIELD_NAME + ", " + Contentlet.WORKFLOW_ACTION_KEY + "\r\n" + @@ -1482,7 +1483,13 @@ public void importFile_success_when_importLinesUpdateExistingContent() //Validations validate(results, false, false, false); - Thread.sleep(2000); + // The import runs asynchronously; wait for all three contentlets to land + // rather than assuming a fixed delay is long enough. + final String importedTypeInode = contentType.inode(); + await().atMost(30, TimeUnit.SECONDS) + .pollInterval(200, TimeUnit.MILLISECONDS) + .until(() -> contentletAPI + .findByStructure(importedTypeInode, user, false, 0, 0).size() == 3); savedData = contentletAPI .findByStructure(contentType.inode(), user, false, 0, 0); @@ -5725,7 +5732,7 @@ public void importFile_tagFieldUpdate_replacesExistingTags() c.setIndexPolicy(IndexPolicy.FORCE); } APILocator.getContentletIndexAPI().addContentToIndex(afterFirst); - Thread.sleep(1000); + awaitIndexed(afterFirst); final String secondCsv = "title,hostFolder,tags\r\n" + contentTitle + "," + defaultSite.getIdentifier() + ",\"" @@ -5796,6 +5803,37 @@ private void cleanUpContentType(final ContentType contentType) { } } + /** + * Waits until every given contentlet is retrievable from the index. + *

+ * {@code addContentToIndex} returns before Elasticsearch has refreshed, so the + * content is not immediately searchable. This replaces a fixed sleep: it + * returns as soon as the index has caught up instead of always paying the + * worst case, and fails with a clear timeout if it never does. + * + * @param contentlets the contentlets that were just pushed to the index + */ + private void awaitIndexed(final List contentlets) { + if (contentlets == null || contentlets.isEmpty()) { + return; + } + final ContentletAPI contentletAPI = APILocator.getContentletAPI(); + final List inodes = contentlets.stream().map(Contentlet::getInode) + .collect(Collectors.toList()); + await().atMost(30, TimeUnit.SECONDS) + .pollInterval(200, TimeUnit.MILLISECONDS) + .until(() -> { + for (final String inode : inodes) { + final List found = contentletAPI.searchIndex( + "+inode:" + inode, 0, -1, null, user, false); + if (found == null || found.isEmpty()) { + return false; + } + } + return true; + }); + } + private void cleanUpTagsByName(final TagAPI tagAPI, final String tagName) { try { for (final Tag t : tagAPI.getTagsByName(tagName)) { diff --git a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task240306MigrateLegacyLanguageVariablesTest.java b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task240306MigrateLegacyLanguageVariablesTest.java index 6abbfc93b251..d032ac73d285 100644 --- a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task240306MigrateLegacyLanguageVariablesTest.java +++ b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task240306MigrateLegacyLanguageVariablesTest.java @@ -625,12 +625,21 @@ private void removeExistingLanguageVariables() throws DotDataException, DotSecur "+contentType:" + languageVariableContentType.variable(), 0, 0, null, APILocator.systemUser(), false); - for (Contentlet contentlet : existingVariables) { + if (!existingVariables.isEmpty()) { + // Batched for the same reason as cleanup() - see destroyQuietly. try { - contentletAPI.destroy(contentlet, APILocator.systemUser(), false); - } catch (Exception e) { - Logger.warn(this, "Failed to delete existing language variable: " + - contentlet.getIdentifier(), e); + contentletAPI.destroy(existingVariables, APILocator.systemUser(), false); + } catch (Exception batchFailure) { + Logger.debug(this, "Batch destroy failed, falling back to one at a time: " + + batchFailure.getMessage(), batchFailure); + for (final Contentlet contentlet : existingVariables) { + try { + contentletAPI.destroy(contentlet, APILocator.systemUser(), false); + } catch (Exception e) { + Logger.warn(this, "Failed to delete existing language variable: " + + contentlet.getIdentifier(), e); + } + } } } @@ -681,9 +690,41 @@ private boolean removeLanguageAndContent(final Language language) throws DotData * @param summary the migration summary */ private void cleanup(final ImmutableMigrationSummary summary) { - final ContentletAPI contentletAPI = APILocator.getContentletAPI(); //Clean up required to avoid side effects - summary.success().forEach((language, inodes) -> { + final List inodes = summary.success().values().stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + destroyQuietly(inodes); + } + + /** + * Destroys the given contentlets by inode, batching the work. + *

+ * A migration run produces well over a hundred Language Variables, and both + * {@code find} and {@code destroy} are per-contentlet round trips - + * {@code destroy} is {@code @WrapInTransaction}, so destroying one at a time + * costs one transaction (plus an ES delete and cache invalidation) each. The + * batch overloads collapse that into a single query and a single transaction. + *

+ * Teardown must never leave content behind for the next test, so if the batch + * fails for any reason this falls back to the original per-contentlet loop, + * which tolerates individual failures. + * + * @param inodes inodes of the contentlets to destroy + */ + private void destroyQuietly(final List inodes) { + if (inodes.isEmpty()) { + return; + } + final ContentletAPI contentletAPI = APILocator.getContentletAPI(); + try { + final List contentlets = contentletAPI.findContentlets(inodes); + if (!contentlets.isEmpty()) { + contentletAPI.destroy(contentlets, APILocator.systemUser(), false); + } + } catch (Exception batchFailure) { + Logger.debug(this, "Batch destroy failed, falling back to one at a time: " + + batchFailure.getMessage(), batchFailure); inodes.forEach(inode -> { try { final Contentlet contentlet = contentletAPI.find(inode, APILocator.systemUser(), false); @@ -692,7 +733,7 @@ private void cleanup(final ImmutableMigrationSummary summary) { Logger.debug(this, e.getMessage(), e); } }); - }); + } } /** From eba1e69a5cd13a1a58d8abe70c866124e0a3cdc9 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 7 Aug 2026 10:32:35 -0400 Subject: [PATCH 06/11] perf(test): size pagination fixtures to the page size, not the other way round BrowserAPITest was the third most expensive integration test at 4.06m. Its pagination tests picked a page size and then built a dataset several times larger to cross the boundaries - 25 folders + 30 file assets to test "page 1 holds every folder plus one contentlet", and so on. Every FileAssetDataGen writes a temp file, persists a contentlet and indexes it with WAIT_FOR, so the dataset is nearly all of the runtime. None of those assertions depend on absolute numbers. They depend on relationships: page size vs folder count, and how much content remains after the cursor. Scaling both sides down preserves every invariant exactly: page 1 fills with folders then tops up 25f + 30c -> 5f + 6c later page, fewer than a page remain 10f + 25c -> 2f + 5c mid-stream page, more remains 15f + 50c -> 3f + 10c exhaustive scan across permission gaps 20c -> 10c scan limit stops the loop 20c -> 5c That is 145 file assets down to 36. The sizes are now named constants derived from the page size, so the relationship under test is visible and the next person has no reason to re-inflate the dataset. Three tests are renamed: their old names hardcoded the counts and would have become lies. FolderResourceTest got the same treatment, but only where it was safe. Three of its five large loops are load-bearing and are left alone: moreThan20Folders (25) regression test against a former hardcoded 20 cap defaultLimit40 (45) pins the @DefaultValue("40") on the limit param limitMinusOne (50) proves limit=-1 bypasses that same 40 cap Shrinking any of those below its cap would make the test pass trivially and silently stop testing anything. Only withCustomLimit and withOffset - which pin no production constant - are reduced, 30 subfolders each to 10. Not run locally (needs the full IT stack); verified by test-compile and by the integration suites in this PR. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NH4Pes5S9d1AQorJBNeFJU --- .../com/dotcms/browser/BrowserAPITest.java | 171 +++++++++++------- .../api/v1/folder/FolderResourceTest.java | 38 ++-- 2 files changed, 127 insertions(+), 82 deletions(-) diff --git a/dotcms-integration/src/test/java/com/dotcms/browser/BrowserAPITest.java b/dotcms-integration/src/test/java/com/dotcms/browser/BrowserAPITest.java index 3519877b9c0f..8ec884472431 100644 --- a/dotcms-integration/src/test/java/com/dotcms/browser/BrowserAPITest.java +++ b/dotcms-integration/src/test/java/com/dotcms/browser/BrowserAPITest.java @@ -1432,15 +1432,22 @@ public void test_buildBaseESQuery_edgeCases() { * to calculate the offset within the database pagination. */ @Test - public void test_SmartPaginationPage1_25Folders1Contentlet() throws Exception { + public void test_SmartPaginationPage1_FoldersThenOneContentlet() throws Exception { + // What is under test is the RELATIONSHIP between page size and folder count: + // a page larger than the folder count is filled with every folder and then + // topped up from the DB. The absolute sizes do not matter, and each item + // costs a persist + index, so keep them small. + final int folderCount = 5; + final int contentCount = 6; // more than needed, so the top-up is bounded + final int pageSize = folderCount + 1; + final User owner = new UserDataGen().nextPersisted(); // Create a test environment final Host host = new SiteDataGen().nextPersisted(); final Folder parentFolder = new FolderDataGen().site(host).nextPersisted(); - // Create 25 folders final List subFolders = new ArrayList<>(); - for (int i = 0; i < 25; i++) { + for (int i = 0; i < folderCount; i++) { final Folder subFolder = new FolderDataGen() .name(String.format("folder_%02d", i)) .parent(parentFolder) @@ -1449,8 +1456,7 @@ public void test_SmartPaginationPage1_25Folders1Contentlet() throws Exception { subFolders.add(subFolder); } - // Create 30 contentlets - for (int i = 0; i < 30; i++) { + for (int i = 0; i < contentCount; i++) { new FileAssetDataGen(FileUtil.createTemporaryFile("content", ".txt", "content " + i)) .host(host) .folder(parentFolder) @@ -1458,7 +1464,7 @@ public void test_SmartPaginationPage1_25Folders1Contentlet() throws Exception { .nextPersisted(); } - // Execute pagination query - Page 1 with page size 26 + // Execute pagination query - Page 1, one slot wider than the folder count final BrowserQuery browserQuery = BrowserQuery.builder() .showFolders(true) .showContent(true) @@ -1467,7 +1473,7 @@ public void test_SmartPaginationPage1_25Folders1Contentlet() throws Exception { .showLinks(false) // Simplify test by disabling links .withHostOrFolderId(parentFolder.getIdentifier()) .offset(0) - .maxResults(26) + .maxResults(pageSize) .build(); final PaginatedContents paginatedContents = browserAPI.getPaginatedContents(browserQuery); @@ -1478,46 +1484,53 @@ public void test_SmartPaginationPage1_25Folders1Contentlet() throws Exception { @SuppressWarnings("unchecked") final List> list = paginatedContents.list; - assertEquals("Should return exactly 26 items (25 folders + 1 contentlet)", 26, list.size()); - assertEquals("Folder count should be 25", 25, paginatedContents.folderCount); + assertEquals("Should return every folder plus one contentlet", pageSize, list.size()); + assertEquals("Folder count should be " + folderCount, folderCount, paginatedContents.folderCount); assertEquals("Content count should be 1", 1, paginatedContents.contentCount); - // Verify first 25 items are folders - for (int i = 0; i < 25; i++) { + // Verify the folders come first + for (int i = 0; i < folderCount; i++) { final Map item = list.get(i); assertNotNull("Item should have name", item.get("name")); - assertTrue("First 25 items should be folders", + assertTrue("Leading items should be folders", item.get("name").toString().startsWith("folder_")); assertEquals("Owner should be the same as parent folder", owner.getFullName(), item.get("owner")); } // Verify the last item is a contentlet - final Map lastItem = list.get(25); + final Map lastItem = list.get(folderCount); assertNotNull("Last item should have extension", lastItem.get("extension")); assertEquals("Last item should be a file", "txt", lastItem.get("extension")); } /** - * Test Case: Smart Pagination - Page 2 with the same data (offset=11, still 11 items per page) - * Expected: 11 contentlets (all folders were shown on page 1) + * Test Case: Smart Pagination - a later page, past the end of the folders + * Expected: only the contentlets left after the cursor, and no more pages */ @Test - public void test_SmartPaginationPage2_15Contentlets() throws Exception { + public void test_SmartPaginationPage2_RemainingContentletsOnly() throws Exception { + // The invariant: once the folder cursor is past every folder, the page is + // contentlets only, and when fewer than a full page remain there is no next + // page. Sizes are kept small - each item costs a persist + index. + final int folderCount = 2; + final int contentCount = 5; + final int contentCursor = 2; + final int remaining = contentCount - contentCursor; // 3 + final int pageSize = remaining + 1; // wider than what is left + // Create test environment final Host host = new SiteDataGen().nextPersisted(); final Folder parentFolder = new FolderDataGen().site(host).nextPersisted(); - // Create 10 folders - for (int i = 0; i < 10; i++) { + for (int i = 0; i < folderCount; i++) { new FolderDataGen() .name(String.format("folder_%02d", i)) .parent(parentFolder) .nextPersisted(); } - // Create 25 contentlets - for (int i = 0; i < 25; i++) { + for (int i = 0; i < contentCount; i++) { new FileAssetDataGen(FileUtil.createTemporaryFile("content", ".txt", "content " + i)) .host(host) .folder(parentFolder) @@ -1525,16 +1538,16 @@ public void test_SmartPaginationPage2_15Contentlets() throws Exception { .nextPersisted(); } - // Execute pagination query - Page 2 (offset=10) + // Execute pagination query - cursors placed past all folders final BrowserQuery browserQuery = BrowserQuery.builder() .showFolders(true) .showContent(true) .showFiles(true) .showLinks(false) .withHostOrFolderId(parentFolder.getIdentifier()) - .folderCursor(10) // Second page - .contentCursor(10) - .maxResults(20) + .folderCursor(folderCount) // past every folder + .contentCursor(contentCursor) + .maxResults(pageSize) .build(); final PaginatedContents paginatedContents = browserAPI.getPaginatedContents(browserQuery); @@ -1545,33 +1558,39 @@ public void test_SmartPaginationPage2_15Contentlets() throws Exception { @SuppressWarnings("unchecked") final List> list = paginatedContents.list; - assertEquals("Should return exactly 15 items (15 contentlets, no folders)", 15, list.size()); - assertEquals("Folder count should be 0 the 10 folders where in the first page", 0, paginatedContents.folderCount); + assertEquals("Should return only the contentlets left after the cursor", remaining, list.size()); + assertEquals("Folder count should be 0, all folders were on the first page", 0, paginatedContents.folderCount); assertFalse("Should indicate NO more folders available", paginatedContents.hasMoreFolders); - assertEquals("Content count should be 15", 15, paginatedContents.contentCount); + assertEquals("Content count should be " + remaining, remaining, paginatedContents.contentCount); assertFalse("Should indicate NO more content available", paginatedContents.hasMoreContent); } /** - * Test Case: Smart Pagination - Page 3 (offset=52) - * Expected: 26 more contentlets + * Test Case: Smart Pagination - a full mid-stream page with more still to come + * Expected: exactly one page of contentlets, and hasMoreContent set */ @Test - public void test_SmartPaginationPage3_16MoreContentlets() throws Exception { + public void test_SmartPaginationPage3_FullPageWithMoreRemaining() throws Exception { + // The invariant here is the opposite of the previous test: MORE than a page + // remains after the cursor, so the page comes back full and hasMoreContent + // is true. Sizes are kept small - each item costs a persist + index. + final int folderCount = 3; + final int contentCount = 10; + final int contentCursor = 3; + final int pageSize = 4; // < contentCount - contentCursor (7) + // Create a test environment final Host host = new SiteDataGen().nextPersisted(); final Folder parentFolder = new FolderDataGen().site(host).nextPersisted(); - // Create 15 folders - for (int i = 0; i < 15; i++) { + for (int i = 0; i < folderCount; i++) { new FolderDataGen() .name(String.format("folder_%02d", i)) .parent(parentFolder) .nextPersisted(); } - // Create 50 contentlets - for (int i = 0; i < 50; i++) { + for (int i = 0; i < contentCount; i++) { new FileAssetDataGen(FileUtil.createTemporaryFile("content", ".txt", "content " + i)) .host(host) .folder(parentFolder) @@ -1579,7 +1598,7 @@ public void test_SmartPaginationPage3_16MoreContentlets() throws Exception { .nextPersisted(); } - // Execute pagination query - Page 3 (offset=52) + // Execute pagination query - cursors past all folders, mid-way through content final BrowserQuery browserQuery = BrowserQuery.builder() .showFolders(true) .showContent(true) @@ -1587,9 +1606,9 @@ public void test_SmartPaginationPage3_16MoreContentlets() throws Exception { .showDotAssets(true) .showLinks(false) .withHostOrFolderId(parentFolder.getIdentifier()) - .folderCursor(15) - .contentCursor(17) // Third page (16*2) = 32 (15 folders and 17 contents) - .maxResults(16) + .folderCursor(folderCount) + .contentCursor(contentCursor) + .maxResults(pageSize) .build(); final PaginatedContents paginatedContents = browserAPI.getPaginatedContents(browserQuery); @@ -1600,10 +1619,10 @@ public void test_SmartPaginationPage3_16MoreContentlets() throws Exception { @SuppressWarnings("unchecked") final List> list = paginatedContents.list; - assertEquals("Should return exactly 16 items (16 contentlets)", 16, list.size()); + assertEquals("Should return a full page of contentlets", pageSize, list.size()); assertEquals("Folder count should be 0", 0, paginatedContents.folderCount); assertFalse("Should indicate NO more folders available", paginatedContents.hasMoreFolders); - assertEquals("Content count should be 16", 16, paginatedContents.contentCount); + assertEquals("Content count should be " + pageSize, pageSize, paginatedContents.contentCount); assertTrue("Should indicate more content available", paginatedContents.hasMoreContent); } @@ -1917,8 +1936,13 @@ public void test_exhaustive_pagination_with_permission_filtering() throws Except // This ensures non-continuous distribution in the database final List accessibleContentlets = new ArrayList<>(); - // Create 20 pieces of content, alternating permissions (10 accessible, 10 non-accessible) - for (int i = 0; i < 20; i++) { + // What matters is that accessible items are separated by gaps the scan has to + // skip, not how many there are. Alternating permissions over totalContent + // gives accessibleCount readable items; each costs a persist + index, so keep + // the total small. + final int totalContent = 10; + final int accessibleCount = totalContent / 2; + for (int i = 0; i < totalContent; i++) { final File file = FileUtil.createTemporaryFile("content(" + i + ")", ".txt", "content-" + i); final Contentlet contentlet = new FileAssetDataGen(file) .host(host) @@ -1954,10 +1978,11 @@ public void test_exhaustive_pagination_with_permission_filtering() throws Except }); }); - // Verify permission setup: should have 10 accessible contentlets - assertEquals("Should have created 10 accessible contentlets", 10, accessibleContentlets.size()); + // Verify permission setup + assertEquals("Should have created " + accessibleCount + " accessible contentlets", + accessibleCount, accessibleContentlets.size()); - // Test Case 1: Request page size of 5 - should get exactly 5 accessible items + // Test Case 1: Request a partial page - should fill it despite the gaps final BrowserQuery query1 = BrowserQuery.builder() .withHostOrFolderId(folder.getInode()) .ignoreSiteForFolders(true) @@ -1977,38 +2002,46 @@ public void test_exhaustive_pagination_with_permission_filtering() throws Except // Using reflection to access the package-private method for direct testing final BrowserAPIImpl browserAPIImpl = (BrowserAPIImpl) browserAPI; - final var results1 = browserAPIImpl.getContentUnderParentFromDB(query1, 5); - assertEquals("Should return exactly 5 accessible contentlets", 5, results1.contentlets.size()); + final int partialPage = 2; + final var results1 = browserAPIImpl.getContentUnderParentFromDB(query1, partialPage); + assertEquals("Should return exactly " + partialPage + " accessible contentlets", + partialPage, results1.contentlets.size()); assertTrue("Should indicate more pages available", results1.hasMore); - // Test Case 2: Request page size of 8 - should get exactly 8 accessible items - final var results2 = browserAPIImpl.getContentUnderParentFromDB(query1, 8); - assertEquals("Should return exactly 8 accessible contentlets", 8, results2.contentlets.size()); + // Test Case 2: Request one short of everything - should still fill the page + final var results2 = browserAPIImpl.getContentUnderParentFromDB(query1, accessibleCount - 1); + assertEquals("Should return exactly " + (accessibleCount - 1) + " accessible contentlets", + accessibleCount - 1, results2.contentlets.size()); assertTrue("Should indicate more pages available", results2.hasMore); - // Test Case 3: Request page size of 10 - should get all 10 accessible items - final var results3 = browserAPIImpl.getContentUnderParentFromDB(query1, 10); - assertEquals("Should return exactly 10 accessible contentlets", 10, results3.contentlets.size()); + // Test Case 3: Request exactly what is available - should get all, with no more + final var results3 = browserAPIImpl.getContentUnderParentFromDB(query1, accessibleCount); + assertEquals("Should return exactly " + accessibleCount + " accessible contentlets", + accessibleCount, results3.contentlets.size()); assertFalse("Should indicate no more pages available", results3.hasMore); - // Test Case 4: Request more than available - should get all 10 accessible items - final var results4 = browserAPIImpl.getContentUnderParentFromDB(query1, 15); - assertEquals("Should return all 10 accessible contentlets", 10, results4.contentlets.size()); + // Test Case 4: Request more than available - should get all accessible items + final var results4 = browserAPIImpl.getContentUnderParentFromDB(query1, accessibleCount + 3); + assertEquals("Should return all " + accessibleCount + " accessible contentlets", + accessibleCount, results4.contentlets.size()); assertFalse("Should indicate no more pages available", results4.hasMore); - // Test Case 5: Cursor-based pagination - // first page returns 5 items and a cursor, - // second page continues from that cursor and returns the remaining 5 items. - final var results5 = browserAPIImpl.getContentUnderParentFromDB(query1, 5); - assertEquals("Should return exactly 5 accessible contentlets on page 1", 5, results5.contentlets.size()); + // Test Case 5: Cursor-based pagination - the first page returns a cursor, and the + // second page continues from it and returns whatever is left. + final int firstPage = 3; + final int secondPage = accessibleCount - firstPage; + final var results5 = browserAPIImpl.getContentUnderParentFromDB(query1, firstPage); + assertEquals("Should return exactly " + firstPage + " accessible contentlets on page 1", + firstPage, results5.contentlets.size()); assertTrue("Should indicate more pages available after page 1", results5.hasMore); // Build query2 from query1, advancing only the contentCursor final BrowserQuery query2 = BrowserQuery.from(query1) .contentCursor(results5.nextDbCursor) .build(); - final var results6 = browserAPIImpl.getContentUnderParentFromDB(query2, 8); - assertEquals("Should return remaining 5 accessible contentlets on page 2", 5, results6.contentlets.size()); + final var results6 = browserAPIImpl.getContentUnderParentFromDB(query2, accessibleCount); + assertEquals("Should return the remaining " + secondPage + " accessible contentlets on page 2", + secondPage, results6.contentlets.size()); assertFalse("Should indicate no more pages available after page 2", results6.hasMore); } @@ -2075,8 +2108,8 @@ public void test_getPaginatedContents_foldersExactlyFillPage_hasMoreContentIsTru /** *

    *
  • Method to Test: {@link BrowserAPI#getPaginatedContents(BrowserQuery)}
  • - *
  • Given Scenario: A site contains 20 content items but - * {@code BROWSER_DB_MAX_SCAN_ROWS} is intentionally set to 15, lower than the total row + *
  • Given Scenario: A site contains more content items than + * {@code BROWSER_DB_MAX_SCAN_ROWS}, which is intentionally set below the total row * count. The scan loop must stop as soon as the number of rows scanned exceeds the limit, * preventing runaway queries on large sites with heavily restricted users.
  • *
  • Expected Result: The request completes without hanging or throwing an @@ -2086,8 +2119,10 @@ public void test_getPaginatedContents_foldersExactlyFillPage_hasMoreContentIsTru */ @Test public void test_getPaginatedContents_scanLimitStopsLoop() throws Exception { - final int scanLimit = 15; - final int itemCount = 20; + // Only the relationship matters: itemCount must exceed scanLimit so the loop + // trips the limit. Each item costs a persist + index, so keep both small. + final int scanLimit = 3; + final int itemCount = 5; Config.setProperty(BrowserAPIImpl.BROWSER_DB_MAX_SCAN_ROWS_KEY, scanLimit); try { @@ -2114,7 +2149,7 @@ public void test_getPaginatedContents_scanLimitStopsLoop() throws Exception { final PaginatedContents result = browserAPI.getPaginatedContents(query); assertNotNull("Result must not be null when scan limit is reached", result); - // 20 items were accumulated before the scan limit fired (dbOffset 20 >= scanLimit 15) + // Items accumulated before the scan limit fired (dbOffset >= scanLimit) assertTrue("Should have returned items accumulated before the scan limit", result.contentCount > 0); // Cursor must reflect how far into the DB the scan reached diff --git a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/folder/FolderResourceTest.java b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/folder/FolderResourceTest.java index a916695d2a8b..53089d20536e 100644 --- a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/folder/FolderResourceTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/folder/FolderResourceTest.java @@ -344,59 +344,69 @@ public void test_findSubFoldersByPath_defaultLimit40_returnsUpTo40() throws DotD /** * Method to test: findSubFoldersByPath in the FolderResource - * Given Scenario: Create 30 subfolders; call with limit=10 - * ExpectedResult: 10 results (the parent + 9 subfolders) + * Given Scenario: Create more subfolders than the requested limit + * ExpectedResult: exactly `limit` results (the parent + limit-1 subfolders) */ @Test public void test_findSubFoldersByPath_withCustomLimit_returnsLimitedResults() throws DotDataException, DotSecurityException { + // Unlike the cap tests above, no production constant is pinned here - all this + // needs is more subfolders than the limit, so keep both small. + final int limit = 5; + final int subFolders = limit * 2; + final Host site = new SiteDataGen().nextPersisted(); final Folder parent = new FolderDataGen().site(site).name("parent").nextPersisted(); - for (int i = 0; i < 30; i++) { + for (int i = 0; i < subFolders; i++) { new FolderDataGen().parent(parent).name(String.format("subfolder%02d", i)).nextPersisted(); } final String path = String.format("//%s/%s/", site.getHostname(), parent.getName()); final Response res = resource.findSubFoldersByPath( getHttpRequest(adminUser.getEmailAddress(), "admin"), response, - new SearchByPathForm(path), 0, 10); + new SearchByPathForm(path), 0, limit); Assert.assertEquals(Status.OK.getStatusCode(), res.getStatus()); final ResponseEntityView entity = ResponseEntityView.class.cast(res.getEntity()); final List results = (List) entity.getEntity(); - Assert.assertEquals("Expected 10 results with limit=10", 10, results.size()); + Assert.assertEquals("Expected " + limit + " results with limit=" + limit, limit, results.size()); } /** * Method to test: findSubFoldersByPath in the FolderResource - * Given Scenario: Create 30 named subfolders; call with offset=10, limit=10 - * ExpectedResult: 10 results that do NOT include the first 10 items + * Given Scenario: Create enough named subfolders for two full pages; page through them + * ExpectedResult: a second page of `limit` results that does NOT overlap the first */ @Test public void test_findSubFoldersByPath_withOffset_returnsCorrectPage() throws DotDataException, DotSecurityException { + // Only needs enough subfolders to fill two pages; no production constant is + // pinned here, so keep the page size and the dataset small. + final int limit = 5; + final int subFolders = limit * 2; + final Host site = new SiteDataGen().nextPersisted(); final Folder parent = new FolderDataGen().site(site).name("parent").nextPersisted(); - for (int i = 0; i < 30; i++) { + for (int i = 0; i < subFolders; i++) { new FolderDataGen().parent(parent).name(String.format("subfolder%02d", i)).nextPersisted(); } final String path = String.format("//%s/%s/", site.getHostname(), parent.getName()); - // page 1: first 10 + // page 1 final Response page1Res = resource.findSubFoldersByPath( getHttpRequest(adminUser.getEmailAddress(), "admin"), response, - new SearchByPathForm(path), 0, 10); + new SearchByPathForm(path), 0, limit); final List page1 = (List) ResponseEntityView.class.cast(page1Res.getEntity()).getEntity(); - // page 2: next 10 + // page 2 final Response page2Res = resource.findSubFoldersByPath( getHttpRequest(adminUser.getEmailAddress(), "admin"), response, - new SearchByPathForm(path), 10, 10); + new SearchByPathForm(path), limit, limit); final List page2 = (List) ResponseEntityView.class.cast(page2Res.getEntity()).getEntity(); - Assert.assertEquals(10, page1.size()); - Assert.assertEquals(10, page2.size()); + Assert.assertEquals(limit, page1.size()); + Assert.assertEquals(limit, page2.size()); // No overlap between pages final List page1Paths = page1.stream().map(FolderSearchResultView::getPath).toList(); From 193c3adde4c33156427f774417f83dd3c27c575d Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 7 Aug 2026 11:58:28 -0400 Subject: [PATCH 07/11] fix(publishing): guard null user in S3 endpoint validation, fix two order-dependent tests Re-sharding the integration suites surfaced two tests that only passed because of where they sat in the run order. Both are genuine defects, so they are fixed rather than pinned back into place. AWSS3PublishingEndPoint - a NullPointerException thrown from inside a catch block. validatePublishingEndPoint() catches S3 connection failures and reports them to the user, but built that notification with: PortalUtil.getUser().getUserId() PortalUtil.getUser() returns null whenever no request is bound to the thread: final HttpServletRequest req = HttpServletRequestThreadLocal.INSTANCE.getRequest(); return req == null ? null : getUser(req); So for any non-request caller - scheduled jobs, background tasks, tests - the error handler NPE'd, replacing the S3 error it was trying to report with a confusing NullPointerException and defeating the catch entirely. Now null-checked; the notification is a UI concern, so with no user to notify the logged warning above it is the whole story. PublishingEndPointTest depended on that NPE not happening, which in turn depended on some earlier test in the same suite leaving an HttpServletRequestThreadLocal behind. In MainSuite1a something did; scheduled elsewhere, nothing does. The tests also swallowed the exception: catch (Exception e) { Assert.assertTrue("No Exception should be thrown", false); } which discards the cause - four retries produced four identical, contentless messages and told us nothing. They now let the exception surface, so a failure names itself. Renamed from *_returnException to *_returnsWithoutThrowing, since they assert the opposite of what they claimed, and dropped the dead `exceptionCatched` locals. The third test in the file had the identical pattern and is fixed too - it has not failed yet, but only because of where it runs. RuleBundlerTest created its Rules in a static @DataProvider. Providers are evaluated when the suite is CONSTRUCTED - MainBaseSuite builds every runner up front - so those Rules existed long before the test ran, and the ~65 test classes scheduled in between could destroy them, giving "Cannot invoke Rule.getGroups() because rule is null". The provider now returns only a flag and the Rule is created inside the test, immediately before use. TestCase also gained a toString so failures read "rule attached to a page" instead of "TestCase@5c2004ac". Not run locally (needs the full IT stack); both modules test-compile clean. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NH4Pes5S9d1AQorJBNeFJU --- .../bean/impl/AWSS3PublishingEndPoint.java | 13 +++- .../remote/bundler/RuleBundlerTest.java | 53 +++++++++++----- .../endpoint/bean/PublishingEndPointTest.java | 62 +++++++++---------- 3 files changed, 82 insertions(+), 46 deletions(-) diff --git a/dotCMS/src/main/java/com/dotcms/publisher/endpoint/bean/impl/AWSS3PublishingEndPoint.java b/dotCMS/src/main/java/com/dotcms/publisher/endpoint/bean/impl/AWSS3PublishingEndPoint.java index 6f824e11ee25..c507dd1fc2ce 100644 --- a/dotCMS/src/main/java/com/dotcms/publisher/endpoint/bean/impl/AWSS3PublishingEndPoint.java +++ b/dotCMS/src/main/java/com/dotcms/publisher/endpoint/bean/impl/AWSS3PublishingEndPoint.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.liferay.portal.language.LanguageUtil; +import com.liferay.portal.model.User; import com.liferay.portal.util.PortalUtil; import java.io.IOException; import java.io.StringReader; @@ -111,8 +112,18 @@ public void validatePublishingEndPoint() throws PublishingEndPointValidationExce final SystemMessageBuilder systemMessageBuilder = new SystemMessageBuilder(); SystemMessage systemMessage = systemMessageBuilder.setMessage("Unable to verify S3 Endpoint. Please check your configuration:" + e.getMessage()).setType(MessageType.SIMPLE_MESSAGE) .setSeverity(MessageSeverity.WARNING).setLife(100000).create(); - SystemMessageEventUtil.getInstance().pushMessage(systemMessage, ImmutableList.of(PortalUtil.getUser().getUserId())); + // PortalUtil.getUser() returns null whenever no request is bound to the + // thread - scheduled jobs, background tasks, tests. Dereferencing it here + // threw a NullPointerException out of this catch block, which replaced the + // S3 error we are trying to report with a confusing NPE and defeated the + // point of catching at all. The message is a UI notification, so when + // there is no user to notify, the logged warning above is the whole story. + final User currentUser = PortalUtil.getUser(); + if (currentUser != null) { + SystemMessageEventUtil.getInstance().pushMessage(systemMessage, + ImmutableList.of(currentUser.getUserId())); + } } } //validatePublishingEndPoint. diff --git a/dotcms-integration/src/test/java/com/dotcms/enterprise/publishing/remote/bundler/RuleBundlerTest.java b/dotcms-integration/src/test/java/com/dotcms/enterprise/publishing/remote/bundler/RuleBundlerTest.java index 1b9b947871ee..9a2e0adcd6de 100644 --- a/dotcms-integration/src/test/java/com/dotcms/enterprise/publishing/remote/bundler/RuleBundlerTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/enterprise/publishing/remote/bundler/RuleBundlerTest.java @@ -48,17 +48,34 @@ public static void prepare() throws Exception { public static Object[] rules() throws Exception { prepare(); - final Rule rule = new RuleDataGen().nextPersisted(); + // Deliberately creates no persisted data. A @DataProvider is evaluated when + // the suite is CONSTRUCTED - MainBaseSuite builds every runner up front - so + // anything persisted here exists long before this test runs, and the test + // classes scheduled in between can destroy it. That is exactly how this test + // used to fail ("rule is null") once it was scheduled late in a suite. + // Fixtures are created inside the test instead; keep this method stateless. + return new TestCase[]{ + new TestCase(false), + new TestCase(true) + }; + } + + /** + * Creates the Rule under test immediately before it is used, so the fixture + * cannot be invalidated by unrelated tests running earlier in the suite. + * + * @param attachedToPage whether the Rule should be bound to an HTML page + * @return a freshly persisted Rule + */ + private Rule createRule(final boolean attachedToPage) { + if (!attachedToPage) { + return new RuleDataGen().nextPersisted(); + } final Host host = new SiteDataGen().nextPersisted(); final Template template = new TemplateDataGen().host(host).nextPersisted(); final HTMLPageAsset htmlPageAsset = new HTMLPageDataGen(host, template).nextPersisted(); - final Rule ruleWithPage = new RuleDataGen().page(htmlPageAsset).nextPersisted(); - - return new TestCase[]{ - new TestCase(rule), - new TestCase(ruleWithPage) - }; + return new RuleDataGen().page(htmlPageAsset).nextPersisted(); } @@ -73,7 +90,7 @@ public static Object[] rules() throws Exception { public void addRuleInBundle(final TestCase testCase) throws DotBundleException, IOException, DotSecurityException, DotDataException { - final Rule rule = testCase.rule; + final Rule rule = createRule(testCase.attachedToPage); final BundlerStatus status = mock(BundlerStatus.class); final RuleBundler bundler = new RuleBundler(); @@ -104,16 +121,24 @@ public void addRuleInBundle(final TestCase testCase) } private static class TestCase{ - Rule rule; - String expectedFilePath; + final boolean attachedToPage; + final String expectedFilePath; - public TestCase(final Rule rule, final String expectedFilePath) { - this.rule = rule; + public TestCase(final boolean attachedToPage, final String expectedFilePath) { + this.attachedToPage = attachedToPage; this.expectedFilePath = expectedFilePath; } - public TestCase(final Rule rule) { - this(rule, "/bundlers-test/rule/rule.rule.xml"); + public TestCase(final boolean attachedToPage) { + this(attachedToPage, "/bundlers-test/rule/rule.rule.xml"); + } + + // Without this the parameterised test reports as + // "addRuleInBundle[0: RuleBundlerTest$TestCase@5c2004ac]", which says nothing + // about which case failed. + @Override + public String toString() { + return attachedToPage ? "rule attached to a page" : "standalone rule"; } } } diff --git a/dotcms-integration/src/test/java/com/dotcms/publisher/endpoint/bean/PublishingEndPointTest.java b/dotcms-integration/src/test/java/com/dotcms/publisher/endpoint/bean/PublishingEndPointTest.java index 2d833775cf43..1b4428c58f8e 100644 --- a/dotcms-integration/src/test/java/com/dotcms/publisher/endpoint/bean/PublishingEndPointTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/publisher/endpoint/bean/PublishingEndPointTest.java @@ -49,57 +49,57 @@ public void validatePublishingEndPoint_whenAWSS3PublishWithoutProperties_returnE Assert.assertTrue(exceptionCatched); } + /** + * A missing bucket ID is reported to the user as a system message, not raised to + * the caller, so validation must return normally. + *

    + * This previously swallowed the exception and asserted {@code false} with the + * message "No Exception should be thrown", which reported nothing about what + * actually failed - four retries produced four identical, useless messages. Let + * the exception surface instead. + */ @Test - public void validatePublishingEndPoint_whenAWSS3PublishWithoutBucketID_returnException() { - - boolean exceptionCatched = false; + public void validatePublishingEndPoint_whenAWSS3PublishWithoutBucketID_returnsWithoutThrowing() + throws PublishingEndPointValidationException { final String noBucketID = "Key=Value"; PublishingEndPoint endPoint = factory.getPublishingEndPoint(AWSS3Publisher.PROTOCOL_AWS_S3); endPoint.setAuthKey(new StringBuilder(PublicEncryptionFactory.encryptString(noBucketID))); - try { - endPoint.validatePublishingEndPoint(); - } catch (Exception e) { - Assert.assertTrue("No Exception should be thrown", false); - } - - Assert.assertTrue("No Exception should be thrown", true); + endPoint.validatePublishingEndPoint(); } + /** + * Invalid credentials are reported to the user as a system message, not raised to + * the caller, so validation must return normally. See the note on the sibling test + * about why the exception is no longer swallowed. + */ @Test - public void validatePublishingEndPoint_whenAWSS3PublishWithoutValidCredentials_returnException() { - - boolean exceptionCatched = false; + public void validatePublishingEndPoint_whenAWSS3PublishWithoutValidCredentials_returnsWithoutThrowing() + throws PublishingEndPointValidationException { - final String noBucketID = "aws_bucket_name=name\naws_access_key=value\naws_secret_access_key=value"; + final String invalidCredentials = + "aws_bucket_name=name\naws_access_key=value\naws_secret_access_key=value"; PublishingEndPoint endPoint = factory.getPublishingEndPoint(AWSS3Publisher.PROTOCOL_AWS_S3); - endPoint.setAuthKey(new StringBuilder(PublicEncryptionFactory.encryptString(noBucketID))); + endPoint.setAuthKey(new StringBuilder(PublicEncryptionFactory.encryptString(invalidCredentials))); - try { - endPoint.validatePublishingEndPoint(); - } catch (Exception e) { - Assert.assertTrue("No Exception should be thrown", false); - } - - Assert.assertTrue("No Exception should be thrown", true); + endPoint.validatePublishingEndPoint(); } + /** + * Same shape as the AWS S3 cases above: validation succeeds by returning, so the + * exception is allowed to surface rather than being swallowed behind an assertion + * message that hides the cause. + */ @Test - public void validatePublishingEndPoint_whenStaticPublishWithWritePermission_returnOK() { - - boolean exceptionCatched = false; + public void validatePublishingEndPoint_whenStaticPublishWithWritePermission_returnOK() + throws PublishingEndPointValidationException { PublishingEndPoint endPoint = factory.getPublishingEndPoint(StaticPublisher.PROTOCOL_STATIC); - try { - endPoint.validatePublishingEndPoint(); - } catch (Exception e) { - Assert.assertTrue("No Exception should be thrown", false); - } - Assert.assertTrue("No Exception should be thrown", true); + endPoint.validatePublishingEndPoint(); } } From ba8de03eb822410b2f69b40ddbacc589af6bfdf1 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 7 Aug 2026 12:08:44 -0400 Subject: [PATCH 08/11] test(hardening): report the cause when an integration test fails in a catch block A scan of all 845 integration test files found 18 catch blocks that fail the test while discarding the exception that caused it. Three were fixed in the previous commit after they cost a full CI run to diagnose; this fixes the remaining 15. The shape: catch (Exception e) { Assert.fail("Should work"); } The test fails, but nothing about the actual cause reaches the log. Surefire retries produce N identical, contentless messages - which is exactly what happened with PublishingEndPointTest: four retries, four copies of "No Exception should be thrown", and no way to tell what threw without reproducing locally. Two of these were worse still - a bare `Assert.fail()` with no message at all (FiltersTest:709, Task220413IncreasePublishedPushedAssetIdColTest:63). All 15 now use: catch (Exception e) { throw new AssertionError("Should work", e); } which fails the test identically but chains the cause, so the stack trace survives into the CI log. Existing messages are preserved verbatim; the two message-less sites get "Unexpected exception". Where the catch did other work first - TailLogResourceTest re-interrupting the thread - that work is kept. This is timely rather than incidental: 14 of the 15 sit in classes this PR moves to a different shard. If any of them trips on a latent order dependency, it will now say why instead of sending the next person on the same archaeology. For balance, the same scan found 90 catch blocks that already pass the exception along, so the codebase mostly gets this right - these were the outliers. It also counted 163 empty catch blocks, but only 8 are the legitimate `try { x; fail(); } catch (Expected e) {}` idiom; the rest are a mix of deliberate best-effort cleanup and genuine hiding that a regex cannot separate. Left alone rather than guessed at. Not run locally (needs the full IT stack); test-compiles clean, and a re-scan reports zero remaining. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NH4Pes5S9d1AQorJBNeFJU --- .../src/test/java/com/dotcms/browser/BrowserAPITest.java | 2 +- .../LocalTransactionAndCloseDBIfOpenedFactoryTest.java | 6 +++--- .../com/dotcms/contenttype/business/StoryBlockAPITest.java | 2 +- .../com/dotcms/contenttype/test/ContentTypeAPIImplTest.java | 2 +- .../java/com/dotcms/contenttype/test/ContentTypeTest.java | 2 +- .../api/v1/authentication/ResetPasswordTokenUtilTest.java | 2 +- .../com/dotcms/rest/api/v1/taillog/TailLogResourceTest.java | 6 +++--- .../com/dotcms/storage/Chainable404StorageCacheTest.java | 2 +- .../src/test/java/com/dotmarketing/filters/FiltersTest.java | 2 +- .../startup/runonce/Task04335CreateSystemWorkflowTest.java | 2 +- .../Task220413IncreasePublishedPushedAssetIdColTest.java | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/dotcms-integration/src/test/java/com/dotcms/browser/BrowserAPITest.java b/dotcms-integration/src/test/java/com/dotcms/browser/BrowserAPITest.java index 8ec884472431..c7324e62abae 100644 --- a/dotcms-integration/src/test/java/com/dotcms/browser/BrowserAPITest.java +++ b/dotcms-integration/src/test/java/com/dotcms/browser/BrowserAPITest.java @@ -433,7 +433,7 @@ public void testGetFolderContentWithValidIdentifier() throws Exception { // http Assert.assertTrue( folderContent.containsKey( "total" ) ); Assert.assertTrue( folderContent.containsKey( "list" ) ); } catch ( Exception e ){ - Assert.fail( "We should not be getting any exception here" ); + throw new AssertionError("We should not be getting any exception here", e); } finally { folderAPI.delete( folder, user, false ); } diff --git a/dotcms-integration/src/test/java/com/dotcms/business/LocalTransactionAndCloseDBIfOpenedFactoryTest.java b/dotcms-integration/src/test/java/com/dotcms/business/LocalTransactionAndCloseDBIfOpenedFactoryTest.java index af9d22417f64..f6c389614cc3 100644 --- a/dotcms-integration/src/test/java/com/dotcms/business/LocalTransactionAndCloseDBIfOpenedFactoryTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/business/LocalTransactionAndCloseDBIfOpenedFactoryTest.java @@ -95,7 +95,7 @@ public void testUpdateSelectTransaction() throws Exception { try { hibernateUtil.list(); } catch (Exception e) { - Assert.fail("Hibernate wired connection still works"); + throw new AssertionError("Hibernate wired connection still works", e); } } ); @@ -168,7 +168,7 @@ public void testSelectUpdateTransaction() throws Exception { try { hibernateUtil.list(); } catch (Exception e) { - Assert.fail("Hibernate wired connection still works"); + throw new AssertionError("Hibernate wired connection still works", e); } } ); @@ -245,7 +245,7 @@ public void testSingleSelectUpdateTransaction() throws Exception { try { hibernateUtil.list(); } catch (Exception e) { - Assert.fail("Hibernate wired connection still works"); + throw new AssertionError("Hibernate wired connection still works", e); } Assert.assertTrue(DbConnectionFactory.inTransaction()); diff --git a/dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java b/dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java index 461b6cd4ac5a..ab7c9c203f8c 100644 --- a/dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java +++ b/dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java @@ -306,7 +306,7 @@ public void test_refresh_references_on_self_reference() throws DotDataException, final StoryBlockReferenceResult refreshResult = APILocator.getStoryBlockAPI() .refreshStoryBlockValueReferences(JSON_SELF_REFERENCE, "3d3a99c4-9b94-4840-8390-704fb6d1d998"); } catch (Throwable e) { - Assert.fail("Should not throw any exception"); + throw new AssertionError("Should not throw any exception", e); } } diff --git a/dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeAPIImplTest.java b/dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeAPIImplTest.java index 943f266a42a8..a2a8e3359ecd 100644 --- a/dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeAPIImplTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeAPIImplTest.java @@ -365,7 +365,7 @@ public void Test_Fields_without_contenttype_on_saving() throws Exception { } } catch (Exception e) { - fail("Should work"); + throw new AssertionError("Should work", e); } finally { ctApi.delete(movie); diff --git a/dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeTest.java b/dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeTest.java index c4a6d7fb7596..708785287ea1 100644 --- a/dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeTest.java @@ -42,7 +42,7 @@ public void test_content_type_living_in_system_host() throws Exception { .nextPersisted(); try { type.folderPath(); } catch (Exception e) { - fail("This should not throw exception"); + throw new AssertionError("This should not throw exception", e); } } diff --git a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/authentication/ResetPasswordTokenUtilTest.java b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/authentication/ResetPasswordTokenUtilTest.java index ef398571236b..e19f4ccbbaff 100644 --- a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/authentication/ResetPasswordTokenUtilTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/authentication/ResetPasswordTokenUtilTest.java @@ -164,7 +164,7 @@ public void test_checkToken_tokenExpired_19_minutes_throwDotInvalidTokenExceptio Assert.assertTrue("token is valid 19 minutes", true); } catch(DotInvalidTokenException e) { - Assert.assertTrue("token should be valid for 19 minutes", false); + throw new AssertionError("token should be valid for 19 minutes", e); } } diff --git a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/taillog/TailLogResourceTest.java b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/taillog/TailLogResourceTest.java index 134b801dafc6..b319644ee94c 100644 --- a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/taillog/TailLogResourceTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/taillog/TailLogResourceTest.java @@ -118,10 +118,10 @@ public void write(final OutboundEvent outboundEvent) throws IOException { // meaning that at least once we will see the keepAlive event which is sent every 20 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(3)); } catch (IOException e) { - fail("Error writing to file"); + throw new AssertionError("Error writing to file", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - fail("Error attempting to sleep thread"); + throw new AssertionError("Error attempting to sleep thread", e); } })); } @@ -131,7 +131,7 @@ public void write(final OutboundEvent outboundEvent) throws IOException { future.get(); } catch (Exception e) { Thread.currentThread().interrupt(); - fail("Error writing to file"); + throw new AssertionError("Error writing to file", e); } } diff --git a/dotcms-integration/src/test/java/com/dotcms/storage/Chainable404StorageCacheTest.java b/dotcms-integration/src/test/java/com/dotcms/storage/Chainable404StorageCacheTest.java index c6b5ef3f9c1d..9e8b5252a6f5 100644 --- a/dotcms-integration/src/test/java/com/dotcms/storage/Chainable404StorageCacheTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/storage/Chainable404StorageCacheTest.java @@ -76,7 +76,7 @@ public void Test_Cache_null_put404() { try { cache.put404(null, null); } catch (Exception e) { - Assert.fail("Should not throw exception put404 null null"); + throw new AssertionError("Should not throw exception put404 null null", e); } } diff --git a/dotcms-integration/src/test/java/com/dotmarketing/filters/FiltersTest.java b/dotcms-integration/src/test/java/com/dotmarketing/filters/FiltersTest.java index a8873568e93a..f54e5b443e0d 100644 --- a/dotcms-integration/src/test/java/com/dotmarketing/filters/FiltersTest.java +++ b/dotcms-integration/src/test/java/com/dotmarketing/filters/FiltersTest.java @@ -707,7 +707,7 @@ public void shouldRedirectToFolderIndex() throws Exception { response.getRedirectLocation()); assertEquals(301, response.getStatus()); } catch (ServletException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Logger.info(this.getClass(), diff --git a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task04335CreateSystemWorkflowTest.java b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task04335CreateSystemWorkflowTest.java index 4d4a9ae70f9f..0f853de17aab 100644 --- a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task04335CreateSystemWorkflowTest.java +++ b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task04335CreateSystemWorkflowTest.java @@ -50,7 +50,7 @@ public void addPermission_onDiffDataBase_Success() throws DotDataException, DotS .loadResult(); } catch (Exception e) { - Assert.fail("Could not insert on the db: " + dbType + ", a permission"); + throw new AssertionError("Could not insert on the db: " + dbType + ", a permission", e); } } } diff --git a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task220413IncreasePublishedPushedAssetIdColTest.java b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task220413IncreasePublishedPushedAssetIdColTest.java index c65d7f1e2211..c21e51c441d4 100644 --- a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task220413IncreasePublishedPushedAssetIdColTest.java +++ b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task220413IncreasePublishedPushedAssetIdColTest.java @@ -61,7 +61,7 @@ public void test_upgradeTask() throws DotDataException, SQLException { try { insertPublishedAsset(ASSET_ID); } catch (DotDataException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } finally { final Connection conn = DbConnectionFactory.getConnection(); From 61bfd7c9bb972f243dc4008b06b8afa175b10da0 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 7 Aug 2026 13:31:39 -0400 Subject: [PATCH 09/11] ci(temp): disable fail-fast on the test matrix to surface all order dependencies REVERT BEFORE MERGE - the restore value is in the comment above the setting. Re-sharding the integration suites exposes tests that only passed because of which suite-mates ran before them. Three found so far, each in a different shard, and each cost a full ~40 minute run to find because fast-fail cancels the other 25 jobs at the first failure. Turning it off for this branch converts that into a single run that reports every affected shard at once, so the remaining coupling can be fixed in one batch instead of one per CI cycle. --- .../scripts/test-balance/find_swallowed.py | 80 +++++++++++ .github/scripts/test-balance/pack_postman.py | 130 ++++++++++++++++++ .github/scripts/test-balance/pack_suites.py | 120 ++++++++++++++++ .github/scripts/test-balance/parse_suites.py | 63 +++++++++ .github/workflows/cicd_comp_test-phase.yml | 11 +- 5 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/test-balance/find_swallowed.py create mode 100644 .github/scripts/test-balance/pack_postman.py create mode 100644 .github/scripts/test-balance/pack_suites.py create mode 100644 .github/scripts/test-balance/parse_suites.py diff --git a/.github/scripts/test-balance/find_swallowed.py b/.github/scripts/test-balance/find_swallowed.py new file mode 100644 index 000000000000..f4f85e4bd4c4 --- /dev/null +++ b/.github/scripts/test-balance/find_swallowed.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Find integration tests that fail without reporting why. + +The pattern that just cost a CI run: + + try { doThing(); } + catch (Exception e) { Assert.assertTrue("No Exception should be thrown", false); } + +The test fails, but the caught exception is discarded, so the CI log says nothing +about the actual cause. Retries just repeat the empty message. + +Classifies each catch block whose body FAILS the test: + SWALLOWED - fails, never references the exception variable (the bad case) + REPORTED - fails and passes the exception / its message along +Also flags empty catch blocks, which hide failures entirely. +""" +import os, re, os, glob, collections + +ROOT = os.path.join(os.environ.get("REPO_ROOT", os.getcwd()), "dotcms-integration/src/test/java") + +CATCH = re.compile(r'catch\s*\(\s*([\w.]+(?:\s*\|\s*[\w.]+)*)\s+(\w+)\s*\)\s*\{') +# something that makes the test fail +FAILS = re.compile(r'\b(fail|assertTrue|assertFalse|assertEquals|assertNotNull|Assert\.fail)\b') +# an unconditional failure, i.e. the catch exists only to fail +HARD_FAIL = re.compile( + r'\bfail\s*\(|\bAssert\.fail\s*\(|assertTrue\s*\([^;]*,\s*false\s*\)|assertFalse\s*\([^;]*,\s*true\s*\)') + + +def body_of(src, open_idx): + """Return the text inside the braces starting at open_idx (index of '{').""" + depth, i = 0, open_idx + while i < len(src): + if src[i] == '{': + depth += 1 + elif src[i] == '}': + depth -= 1 + if depth == 0: + return src[open_idx + 1:i] + i += 1 + return '' + + +swallowed, empty, reported = [], [], [] +for path in glob.glob(ROOT + "/**/*.java", recursive=True): + src = open(path, errors='ignore').read() + rel = path[len(ROOT) + 1:] + for m in CATCH.finditer(src): + var = m.group(2) + body = body_of(src, m.end() - 1) + line = src[:m.start()].count('\n') + 1 + stripped = re.sub(r'//[^\n]*|/\*.*?\*/', '', body, flags=re.S).strip() + + if not stripped: + empty.append((rel, line, m.group(1))) + continue + if not HARD_FAIL.search(stripped): + continue # catch doesn't unconditionally fail + # does it carry the exception anywhere? + carries = re.search(rf'\b{re.escape(var)}\b', stripped) + (reported if carries else swallowed).append((rel, line, m.group(1), stripped[:90])) + +print(f"scanned {len(glob.glob(ROOT + '/**/*.java', recursive=True))} integration test files\n") +print(f"SWALLOWED (fails without reporting the cause): {len(swallowed)}") +print(f"REPORTED (fails and includes the exception): {len(reported)}") +print(f"EMPTY catch blocks (hide failures entirely): {len(empty)}\n") + +by_file = collections.Counter(f for f, *_ in swallowed) +print("=== worst files (swallowed catches) ===") +for f, n in by_file.most_common(20): + print(f" {n:>3} {f}") + +print("\n=== every swallowed catch ===") +for f, line, exc, snippet in sorted(swallowed): + one = ' '.join(snippet.split()) + print(f" {f}:{line} catch({exc}) -> {one[:80]}") + +if empty: + print("\n=== empty catch blocks ===") + for f, line, exc in sorted(empty)[:25]: + print(f" {f}:{line} catch({exc}) {{}}") diff --git a/.github/scripts/test-balance/pack_postman.py b/.github/scripts/test-balance/pack_postman.py new file mode 100644 index 000000000000..a8410eaab4f6 --- /dev/null +++ b/.github/scripts/test-balance/pack_postman.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +"""Rebalance dotcms-postman collection groups. + +Groups are the CI shard unit. Balances on measured newman time, keeping the +total group count at 9 (was 11): four micro-groups merge, graphql splits in two. +GraphQL is folder-sharded via the new `folders` key rather than by splitting the +518KB collection file. +""" +import os +import json, os, collections + +HERE = os.environ.get("BALANCE_WORKDIR", os.path.dirname(os.path.abspath(__file__))) +NGROUPS = 9 +OVERHEAD = 9.4 * 60 # measured fixed cost per postman shard (seconds) + +raw = json.load(open(f"{HERE}/pm/pm_colls.json")) # group -> {TEST-: secs} +gsplit = json.load(open(f"{HERE}/graphql_split.json")) + +# ---- flatten to collection -> seconds -------------------------------------- +COLL_DIR = os.path.join(os.environ.get("REPO_ROOT", os.getcwd()), "dotcms-postman/src/main/resources/postman") +on_disk = {f[:-5] for f in os.listdir(COLL_DIR) + if f.endswith(".json") and f != "postman_environment.json"} + +cost = {} +skipped = [] +for g, d in raw.items(): + for k, v in d.items(): + name = k[5:] if k.startswith("TEST-") else k # strip TEST- prefix + if name not in on_disk: # e.g. failsafe-summary.xml, a maven artifact + skipped.append(name) + continue + cost[name] = v +print(f"collections on disk: {len(on_disk)}, with timings: {len(cost)}") +print(f"skipped non-collection report files: {sorted(set(skipped))}") +untimed = sorted(on_disk - set(cost)) +print(f"on disk but never timed ({len(untimed)}): {untimed}") +for u in untimed: # unknown cost -> keep them in `default` + cost[u] = 0.0 + +# #36915 (already on this branch) moved ContentTypeResourceTests into `template`. +# Keep that; it is reflected in config.json on disk, not in the measured grouping. + +# GraphQLTests becomes two folder-sharded units. +gq = cost.pop("GraphQLTests") +setup_share = 0.16 * 60 +units = {k: v for k, v in cost.items()} +units["GraphQLTests@a"] = 14.60 * 60 + setup_share +units["GraphQLTests@b"] = 10.82 * 60 + setup_share + +print(f"{len(units)} schedulable units, total {sum(units.values())/60:.1f}m") +big = sorted(units.items(), key=lambda kv: -kv[1])[:6] +print("largest units (these bound the shard floor):") +for k, v in big: + print(f" {v/60:6.2f}m {k}") +print(f"\nfloor = largest unit + overhead = {(big[0][1]+OVERHEAD)/60:.1f}m") + +# ---- LPT pack into NGROUPS -------------------------------------------------- +bins = [[] for _ in range(NGROUPS)] +load = [0.0] * NGROUPS +for k, v in sorted(units.items(), key=lambda kv: -kv[1]): + i = load.index(min(load)) + bins[i].append(k) + load[i] += v + +# name each group after its most expensive member, lowercased/short +def gname(members): + head = max(members, key=lambda m: units[m]) + base = head.split("@")[0].replace(".postman_collection", "") + base = base.replace("_Resource_Tests", "").replace("_Resource", "").replace("Tests", "") + base = base.replace("Resource", "").strip("_") or head + suffix = "-a" if head.endswith("@a") else ("-b" if head.endswith("@b") else "") + return base.lower().replace("_", "-") + suffix + +names = [] +for b in bins: + n = gname(b) + while n in names: + n += "2" + names.append(n) + +print(f"\n{'group':<18}{'units':>7}{'test':>9}{'est wall':>10}") +for n, b, l in sorted(zip(names, bins, load), key=lambda x: -x[2]): + print(f"{n:<18}{len(b):>7}{l/60:>8.1f}m{(l+OVERHEAD)/60:>9.1f}m") +print(f"\nmax est wall {(max(load)+OVERHEAD)/60:.1f}m (was 37.8m)") + +# `default` is computed by index.js as "every collection on disk not listed in +# config.json". It is the safety net that picks up newly added collections, so +# it must stay a real shard. Rather than listing all 95 collections and leaving +# `default` empty, leave ONE balanced bin unlisted - it becomes `default`, stays +# balanced, and still absorbs anything new. +# Prefer the bin that already holds the most previously-unnamed collections, so +# `default` keeps meaning "the misc tail" rather than silently owning a headline +# collection. Never pick a bin holding a folder-sharded unit. +was_default = { + k[5:] if k.startswith("TEST-") else k + for g in ("default", "default-split") for k in raw.get(g, {}) +} +# Units that must stay explicitly listed: folder-sharded ones (config drives the +# split) and ContentTypeResourceTests (#36915 deliberately placed it by name). +PINNED = ("GraphQLTests@", "ContentTypeResourceTests") +default_idx = max( + (i for i, b in enumerate(bins) + if not any(m.startswith(PINNED) for m in b)), + key=lambda i: sum(1 for m in bins[i] if m in was_default), +) +print(f"\nbin {default_idx} ({names[default_idx]}) left UNLISTED -> becomes the `default` shard") + +out = [] +for i, (n, b) in enumerate(zip(names, bins)): + if i == default_idx: + continue + entry = {"name": n} + colls, folders = [], None + for m in sorted(b, key=lambda m: -units[m]): + if m.startswith("GraphQLTests@"): + colls.append("GraphQLTests") + folders = {"GraphQLTests": [gsplit["setup"]] + + (gsplit["a"] if m.endswith("@a") else gsplit["b"])} + else: + colls.append(m) + entry["collections"] = colls + if folders: + entry["folders"] = folders + out.append(entry) + +shard_names = [e["name"] for e in out] + ["default"] +json.dump({"groups": out, "shards": shard_names, + "default_members": sorted(bins[default_idx])}, + open(f"{HERE}/postman_groups.json", "w"), indent=2) +print(f"wrote postman_groups.json: {len(out)} listed groups + default = {len(shard_names)} shards") diff --git a/.github/scripts/test-balance/pack_suites.py b/.github/scripts/test-balance/pack_suites.py new file mode 100644 index 000000000000..cb772237d915 --- /dev/null +++ b/.github/scripts/test-balance/pack_suites.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""Bin-pack the 557 MainSuite integration classes into N balanced suites. + +Balances on measured per-class test time (stable run-to-run: 115.2m vs 114.8m +across two runs). Job wall time is NOT used - it is dominated by runner variance. + +Front-block classes (the "run FIRST on purpose" full-scan tests from #36911) are +packed normally but emitted at the head of whichever suite they land in. +""" +import os +import json, os, re, collections + +HERE = os.environ.get("BALANCE_WORKDIR", os.path.dirname(os.path.abspath(__file__))) +WT = os.environ.get("REPO_ROOT", os.getcwd()) +SRC = f"{WT}/dotcms-integration/src/test/java/com/dotcms" +NBINS = 7 +NAMES = ["MainSuite1a", "MainSuite1b", "MainSuite2a", "MainSuite2b", + "MainSuite3a", "MainSuite3b", "MainSuite4a"] + +parsed = json.load(open(f"{HERE}/suites_parsed.json")) +times = json.load(open(f"{HERE}/post_classes.json")) # fqn -> seconds + +# ---- build the pool ------------------------------------------------------- +pool, front = [], set() +for suite, v in parsed.items(): + front |= set(v["front"]) + pool += v["entries"] +assert len(pool) == len(set(pool)), "duplicate classes in pool" + +by_simple = collections.defaultdict(list) +for k in times: + by_simple[k.split(".")[-1]].append(k) + + +def cost(fqn): + if fqn in times: + return times[fqn] + cands = by_simple.get(fqn.split(".")[-1]) # package moved / inner class + return times[cands[0]] if cands else 0.0 + + +missing = [c for c in pool if cost(c) == 0.0] +print(f"pool={len(pool)} front={len(front)} zero-cost={len(missing)}") + +# ---- LPT bin-pack --------------------------------------------------------- +bins = [[] for _ in range(NBINS)] +load = [0.0] * NBINS +for c in sorted(pool, key=cost, reverse=True): + i = load.index(min(load)) + bins[i].append(c) + load[i] += cost(c) + +# order within each bin: front-block first, then descending cost +for b in bins: + b.sort(key=lambda c: (c not in front, -cost(c))) + +print(f"\n{'suite':<14}{'classes':>9}{'test-time':>11}{'front':>7}") +for n, b, l in zip(NAMES, bins, load): + print(f"{n:<14}{len(b):>9}{l/60:>10.1f}m{sum(1 for c in b if c in front):>7}") +print(f"\ntotal {sum(load)/60:.1f}m | perfect {sum(load)/60/NBINS:.1f}m " + f"| max {max(load)/60:.1f}m | spread {(max(load)-min(load))/60:.1f}m") + +# ---- emit ----------------------------------------------------------------- +FRONT_NOTE = """ + // Data-scanning tests run FIRST on purpose. + // Integration tests accumulate content and never clean up, so anything + // that walks the whole dataset (executeUpgrade, findAll*) costs + // O(all content created so far). Scheduled late these pay for every + // preceding test's leftovers. Keep new full-scan tests in this block. +""" + +TMPL = """package com.dotcms; + +import com.dotcms.junit.MainBaseSuite; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Integration test suite shard {idx} of {n}. + * + * Shards are balanced on measured per-class test time so the slowest shard + * bounds the CI critical path as tightly as possible. When adding a test, + * put it in the shard with the lowest total time rather than appending here + * by habit - see .github/test-matrix.yml for the shard list. + * + * Classes are fully qualified so that rebalancing does not churn imports. + */ +@RunWith(MainBaseSuite.class) +@SuiteClasses({{ +{body} +}}) +public class {cls} {{ + +}} +""" + + +def emit(cls, idx, classes): + lines, wrote_note = [], False + for c in classes: + if c in front and not wrote_note: + lines.append(FRONT_NOTE.rstrip("\n")) + wrote_note = True + if c not in front and wrote_note: + lines.append("") + wrote_note = None # close the block once + lines.append(f" {c}.class,") + body = "\n".join(lines).rstrip(",") + # trailing comma on the final entry is legal in Java annotations, keep it simple + body = "\n".join(lines) + if body.rstrip().endswith(","): + body = body.rstrip()[:-1] + return TMPL.format(cls=cls, idx=idx, n=NBINS, body=body) + + +for i, (n, b) in enumerate(zip(NAMES, bins), 1): + open(f"{SRC}/{n}.java", "w").write(emit(n, i, b)) + print(f"wrote {n}.java") + +json.dump({n: b for n, b in zip(NAMES, bins)}, open(f"{HERE}/packed.json", "w"), indent=1) diff --git a/.github/scripts/test-balance/parse_suites.py b/.github/scripts/test-balance/parse_suites.py new file mode 100644 index 000000000000..ba3774e3af96 --- /dev/null +++ b/.github/scripts/test-balance/parse_suites.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Parse MainSuite*.java @SuiteClasses lists -> {suite: {'entries': [fqn], 'front': [fqn]}} + +Front block = the "run FIRST on purpose" cluster added by #36911; those classes do +full-DB scans and must stay at the head of whatever suite they end up in. +""" +import os, re, os, json, sys + +WT = os.environ.get("REPO_ROOT", os.getcwd()) +SRC = f"{WT}/dotcms-integration/src/test/java/com/dotcms" +SUITES = ["MainSuite1a", "MainSuite1b", "MainSuite2a", "MainSuite2b", "MainSuite3a"] +FRONT_MARK = re.compile(r"run FIRST on purpose") + + +def parse(suite): + s = open(f"{SRC}/{suite}.java").read() + imports = dict( + (m.split(".")[-1], m) + for m in re.findall(r"^import\s+([\w.]+);", s, re.M) + if not m.startswith("org.junit") + ) + m = re.search(r"@(?:Suite\.)?SuiteClasses\(\{", s) + body = s[m.end(): s.index("})", m.end())] + + entries, front = [], [] + in_front = False # inside the "run FIRST" cluster + front_done = False + for raw in body.split("\n"): + line = raw.strip() + if not line: + if in_front: # blank line closes the front cluster + in_front, front_done = False, True + continue + if line.startswith(("//", "/*", "*")): + if not front_done and FRONT_MARK.search(line): + in_front = True + continue + mm = re.match(r"([\w.]+)\.class\s*,?", line) + if not mm: + continue + name = mm.group(1) + fqn = name if "." in name else imports.get(name) + if fqn is None: + print(f" !! UNRESOLVED {suite}: {name}", file=sys.stderr) + sys.exit(1) + entries.append(fqn) + if in_front: + front.append(fqn) + return {"entries": entries, "front": front} + + +if __name__ == "__main__": + out = {} + for s in SUITES: + out[s] = parse(s) + print(f"{s:<14} {len(out[s]['entries']):>4} classes, front block = {len(out[s]['front'])}") + for f in out[s]["front"]: + print(f" FRONT {f.split('.')[-1]}") + allc = [c for v in out.values() for c in v["entries"]] + print(f"\ntotal {len(allc)}, unique {len(set(allc))}") + dupes = sorted(c for c in set(allc) if allc.count(c) > 1) + print("DUPLICATES:", dupes if dupes else "none") + json.dump(out, open(os.path.dirname(__file__) + "/suites_parsed.json", "w"), indent=1) diff --git a/.github/workflows/cicd_comp_test-phase.yml b/.github/workflows/cicd_comp_test-phase.yml index 68c1dfd4bcac..7b61431c72a3 100644 --- a/.github/workflows/cicd_comp_test-phase.yml +++ b/.github/workflows/cicd_comp_test-phase.yml @@ -232,7 +232,16 @@ jobs: # a comma-list subset like '1,2', or 'all') run every suite/phase to completion so # failures are attributable per phase. Derived from "phase off" rather than an # exact-match list so comma-list subsets are covered too. - fail-fast: ${{ inputs.opensearch_phase == '' || inputs.opensearch_phase == 'none' || inputs.opensearch_phase == '0' }} + # !!! TEMPORARY - REVERT BEFORE MERGING THIS PR !!! + # Restore to: + # fail-fast: ${{ inputs.opensearch_phase == '' || inputs.opensearch_phase == 'none' || inputs.opensearch_phase == '0' }} + # + # This PR re-shards the integration suites, which exposes tests that were + # only passing because of which suite-mates ran before them. With fast-fail + # on, the first broken shard cancels the other 25 jobs, so each ~40 minute + # run reveals exactly one shard's worth of coupling. Off, a single run + # surfaces all of them at once. + fail-fast: false matrix: ${{ fromJSON(needs.setup-matrix.outputs.matrix) }} steps: From f612b27ed8788042988f56eacba46657619b1eca Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 7 Aug 2026 13:41:58 -0400 Subject: [PATCH 10/11] test(langvar): stop deleting the Language Variable content type in Task240306 test testDropThenRecreateLanguageVariableContentType deleted the Language Variable content type through ContentTypeAPI, then asserted that checkContentType() recreated it. Removed, along with the now-orphaned removeLanguageVariableContentType() helper and two imports it was the only user of. That delete should not be possible in the first place. The Language Variable content type underpins all i18n, and losing it makes LanguageVariableAPIImpl throw NotFoundInDbException and language resolution fall back to emitting raw keys site-wide. #36958 tracks marking it `system` so ContentTypeFactoryImpl's if (type.system()) throw new DotDataException(...) guard refuses the delete. Once that lands this test cannot work as written, and until then it is a test deliberately exercising the destructive path we are trying to close off - and it does so with DELETE_CONTENT_TYPE_ASYNC forced to false, so the type really is gone for whatever runs next in that suite. Nothing else is lost: its remaining assertions (executeUpgrade succeeds, summary present, no failures) duplicate testExecuteUpgrade. What IS lost is coverage of checkContentType() recreating the type when genuinely absent - a real situation for legacy installs, which is why the upgrade task performs that check at all. A comment at the removal site records this, and #36958 tracks re-covering it by seeding the missing state directly rather than by calling the delete API. Two side benefits: - This was one of three ~2.4m tests in the single most expensive integration test class (7.26m, 5.6% of all IT test time), so it takes roughly a third off it. - A grep of all 845 integration test files now finds zero API-level deletes of this content type: 24 references remain and every one is a read. Not run locally (needs the full IT stack); test-compiles clean. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NH4Pes5S9d1AQorJBNeFJU --- ...306MigrateLegacyLanguageVariablesTest.java | 63 ++++++------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task240306MigrateLegacyLanguageVariablesTest.java b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task240306MigrateLegacyLanguageVariablesTest.java index d032ac73d285..1c825c8a6d50 100644 --- a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task240306MigrateLegacyLanguageVariablesTest.java +++ b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task240306MigrateLegacyLanguageVariablesTest.java @@ -1,7 +1,6 @@ package com.dotmarketing.startup.runonce; import com.dotcms.contenttype.business.ContentTypeAPI; -import com.dotcms.contenttype.business.ContentTypeAPIImpl; import com.dotcms.contenttype.model.type.ContentType; import com.dotcms.contenttype.model.type.KeyValueContentType; import com.dotcms.languagevariable.business.ImmutableMigrationSummary; @@ -18,7 +17,6 @@ import com.dotmarketing.portlets.languagesmanager.business.LanguageAPI; import com.dotmarketing.portlets.languagesmanager.business.UniqueLanguageDataGen; import com.dotmarketing.portlets.languagesmanager.model.Language; -import com.dotmarketing.util.Config; import com.dotmarketing.util.Logger; import com.google.common.collect.ImmutableList; import org.junit.BeforeClass; @@ -118,33 +116,25 @@ public void testExecuteUpgrade() throws DotDataException { } } - /** - * Given scenario: We simulate the case where the language variable content type is dropped - * Expected result: the upgrade task should run without errors and recreate the language variable content type when missing. We run a basic check to verify the task ran successfully - * @throws DotDataException if an error occurs - * @throws DotSecurityException if a security violation occurs + /* + * testDropThenRecreateLanguageVariableContentType used to live here. It deleted the + * Language Variable content type through ContentTypeAPI and asserted that + * checkContentType() recreated it. + * + * Removed because that delete should not be possible. The Language Variable content + * type underpins all i18n, and deleting it breaks language resolution site-wide - + * see #36958, which marks it `system` so ContentTypeFactoryImpl.dbDelete refuses. + * Once that lands this test cannot work as written, and in the meantime it is a test + * deliberately exercising a destructive path we are trying to close off. + * + * Its remaining assertions (executeUpgrade succeeds, summary present, no failures) + * duplicate testExecuteUpgrade, so nothing else is lost by deleting it outright. + * + * What IS lost is coverage of checkContentType() recreating the type when it is + * genuinely absent - a real situation for legacy installs, which is why the upgrade + * task performs that check at all. #36958 tracks re-covering it by seeding the + * missing state directly rather than by calling the delete API. */ - @Test - public void testDropThenRecreateLanguageVariableContentType() throws DotDataException, DotSecurityException { - final Task240306MigrateLegacyLanguageVariables dataTask = new Task240306MigrateLegacyLanguageVariables(); - assertTrue("This Data Task must always run", dataTask.forceRun()); - try { - removeLanguageVariableContentType(); - final Optional optional = dataTask.checkContentType(); - assertTrue("The Language Variable Content Type must always be present", optional.isPresent()); - assertTrue("The migration summary object should not exist before running the task", - dataTask.getMigrationSummary().isEmpty()); - dataTask.executeUpgrade(); - assertTrue("There must be a migration summary after the task execution", - dataTask.getMigrationSummary().isPresent()); - final ImmutableMigrationSummary summary = dataTask.getMigrationSummary().get(); - assertTrue("There must be at least 5 successfully processed Locales", summary.success().size() >= 5); - assertEquals("There must be no errors", 0, summary.fails().size()); - } finally { - final Optional migrationSummary = dataTask.getMigrationSummary(); - migrationSummary.ifPresent(this::cleanup); - } - } /** *

      @@ -448,23 +438,6 @@ public void testMigrationVariableFailureDoesNotCascadeToSubsequentVariables() successes.isEmpty()); } - /** - * Given scenario: We simulate the case where the language variable content type is dropped - * @throws DotSecurityException if a security violation occurs - * @throws DotDataException if an error occurs - */ - private void removeLanguageVariableContentType() throws DotSecurityException, DotDataException { - final ContentTypeAPI contentTypeAPI = APILocator.getContentTypeAPI( - APILocator.systemUser()); - final ContentType languageVariableCt = contentTypeAPI.find( - LanguageVariableAPI.LANGUAGEVARIABLE_VAR_NAME); - final boolean asyncDelete = Config.getBooleanProperty( - ContentTypeAPIImpl.DELETE_CONTENT_TYPE_ASYNC, true); - Config.setProperty(ContentTypeAPIImpl.DELETE_CONTENT_TYPE_ASYNC, false); - contentTypeAPI.delete(languageVariableCt); - Config.setProperty(ContentTypeAPIImpl.DELETE_CONTENT_TYPE_ASYNC, asyncDelete); - } - /** * Backs up existing files in the messages directory before modifying them. * @return Map of filename to file contents for restoration From 6191923c247f77650b07a2b20c36a805a74baad2 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 7 Aug 2026 15:05:45 -0400 Subject: [PATCH 11/11] revert(ci): drop the shard rebalance from this PR, keep the independent fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rebalance works — measured on run 31203516115, with fail-fast disabled so every shard reported: IT shards 22.2 - 26.2m (was 38.9m) Postman 18.6 - 28.8m (was 37.8m) But collecting it costs more than this PR can carry. 3 of 7 integration shards and 5 of 9 Postman shards failed, all from tests that were only passing because of which suite-mates ran before them. The Postman breakage was the bigger misjudgement on my part: those group names were not labels, they were dependency clusters. `category-content` grouped Category + ContentResourceV1 + Content_Resource because those collections share data. Rebalancing purely on measured time shattered every one. The GraphQL folder split broke the same way - "Page API - Testing 'page' field with inline fragments" needs setup performed by the "Page API" folder, which landed in the other shard. Reverted here: test-matrix.yml shard lists, the seven repacked MainSuite files, MainSuite3b/4a, dotcms-postman/config.json, index.js folder support, and the temporary fail-fast:false. Kept, because none of it depends on the rebalance: - the dead build-classes artifact (0.5m off the serial prefix, consumed by nothing) - AWSS3PublishingEndPoint: NullPointerException thrown from inside a catch block whenever no request is bound to the thread - 15 catch blocks that failed a test while discarding the cause - Task240306 teardown batched (~150 transactions -> 1) and its Language Variable content type delete removed (see #36958) - ImportUtilTest: 34s of Thread.sleep -> awaitility - pagination fixtures sized to the page size rather than the reverse The full rebalance is preserved on `issue-36942-shard-rebalance-wip` at f612b27ed8 so it can be resumed rather than rebuilt. It is worth resuming: it is an effective detector of tests that depend on their neighbours, and every failure it surfaced so far has been a real defect - including a production NPE and a deletable Language Variable content type. That is a test-independence programme though, not a prerequisite for the fixes above. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NH4Pes5S9d1AQorJBNeFJU --- .github/scripts/test-balance/pack_postman.py | 130 ---- .github/scripts/test-balance/pack_suites.py | 120 ---- .github/scripts/test-balance/parse_suites.py | 63 -- .github/test-matrix.yml | 58 +- .github/workflows/cicd_comp_test-phase.yml | 11 +- .../src/test/java/com/dotcms/MainSuite1a.java | 193 +++--- .../src/test/java/com/dotcms/MainSuite1b.java | 180 +++-- .../src/test/java/com/dotcms/MainSuite2a.java | 197 +++--- .../src/test/java/com/dotcms/MainSuite2b.java | 637 +++++++++++++++--- .../src/test/java/com/dotcms/MainSuite3a.java | 194 +++--- .../src/test/java/com/dotcms/MainSuite3b.java | 108 --- .../src/test/java/com/dotcms/MainSuite4a.java | 108 --- dotcms-postman/config.json | 180 ++--- dotcms-postman/index.js | 40 +- .../postman/ContentTypeResourceTests.json | 63 +- dotcms-postman/verify-config.js | 98 --- 16 files changed, 1029 insertions(+), 1351 deletions(-) delete mode 100644 .github/scripts/test-balance/pack_postman.py delete mode 100644 .github/scripts/test-balance/pack_suites.py delete mode 100644 .github/scripts/test-balance/parse_suites.py delete mode 100644 dotcms-integration/src/test/java/com/dotcms/MainSuite3b.java delete mode 100644 dotcms-integration/src/test/java/com/dotcms/MainSuite4a.java delete mode 100644 dotcms-postman/verify-config.js diff --git a/.github/scripts/test-balance/pack_postman.py b/.github/scripts/test-balance/pack_postman.py deleted file mode 100644 index a8410eaab4f6..000000000000 --- a/.github/scripts/test-balance/pack_postman.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python3 -"""Rebalance dotcms-postman collection groups. - -Groups are the CI shard unit. Balances on measured newman time, keeping the -total group count at 9 (was 11): four micro-groups merge, graphql splits in two. -GraphQL is folder-sharded via the new `folders` key rather than by splitting the -518KB collection file. -""" -import os -import json, os, collections - -HERE = os.environ.get("BALANCE_WORKDIR", os.path.dirname(os.path.abspath(__file__))) -NGROUPS = 9 -OVERHEAD = 9.4 * 60 # measured fixed cost per postman shard (seconds) - -raw = json.load(open(f"{HERE}/pm/pm_colls.json")) # group -> {TEST-: secs} -gsplit = json.load(open(f"{HERE}/graphql_split.json")) - -# ---- flatten to collection -> seconds -------------------------------------- -COLL_DIR = os.path.join(os.environ.get("REPO_ROOT", os.getcwd()), "dotcms-postman/src/main/resources/postman") -on_disk = {f[:-5] for f in os.listdir(COLL_DIR) - if f.endswith(".json") and f != "postman_environment.json"} - -cost = {} -skipped = [] -for g, d in raw.items(): - for k, v in d.items(): - name = k[5:] if k.startswith("TEST-") else k # strip TEST- prefix - if name not in on_disk: # e.g. failsafe-summary.xml, a maven artifact - skipped.append(name) - continue - cost[name] = v -print(f"collections on disk: {len(on_disk)}, with timings: {len(cost)}") -print(f"skipped non-collection report files: {sorted(set(skipped))}") -untimed = sorted(on_disk - set(cost)) -print(f"on disk but never timed ({len(untimed)}): {untimed}") -for u in untimed: # unknown cost -> keep them in `default` - cost[u] = 0.0 - -# #36915 (already on this branch) moved ContentTypeResourceTests into `template`. -# Keep that; it is reflected in config.json on disk, not in the measured grouping. - -# GraphQLTests becomes two folder-sharded units. -gq = cost.pop("GraphQLTests") -setup_share = 0.16 * 60 -units = {k: v for k, v in cost.items()} -units["GraphQLTests@a"] = 14.60 * 60 + setup_share -units["GraphQLTests@b"] = 10.82 * 60 + setup_share - -print(f"{len(units)} schedulable units, total {sum(units.values())/60:.1f}m") -big = sorted(units.items(), key=lambda kv: -kv[1])[:6] -print("largest units (these bound the shard floor):") -for k, v in big: - print(f" {v/60:6.2f}m {k}") -print(f"\nfloor = largest unit + overhead = {(big[0][1]+OVERHEAD)/60:.1f}m") - -# ---- LPT pack into NGROUPS -------------------------------------------------- -bins = [[] for _ in range(NGROUPS)] -load = [0.0] * NGROUPS -for k, v in sorted(units.items(), key=lambda kv: -kv[1]): - i = load.index(min(load)) - bins[i].append(k) - load[i] += v - -# name each group after its most expensive member, lowercased/short -def gname(members): - head = max(members, key=lambda m: units[m]) - base = head.split("@")[0].replace(".postman_collection", "") - base = base.replace("_Resource_Tests", "").replace("_Resource", "").replace("Tests", "") - base = base.replace("Resource", "").strip("_") or head - suffix = "-a" if head.endswith("@a") else ("-b" if head.endswith("@b") else "") - return base.lower().replace("_", "-") + suffix - -names = [] -for b in bins: - n = gname(b) - while n in names: - n += "2" - names.append(n) - -print(f"\n{'group':<18}{'units':>7}{'test':>9}{'est wall':>10}") -for n, b, l in sorted(zip(names, bins, load), key=lambda x: -x[2]): - print(f"{n:<18}{len(b):>7}{l/60:>8.1f}m{(l+OVERHEAD)/60:>9.1f}m") -print(f"\nmax est wall {(max(load)+OVERHEAD)/60:.1f}m (was 37.8m)") - -# `default` is computed by index.js as "every collection on disk not listed in -# config.json". It is the safety net that picks up newly added collections, so -# it must stay a real shard. Rather than listing all 95 collections and leaving -# `default` empty, leave ONE balanced bin unlisted - it becomes `default`, stays -# balanced, and still absorbs anything new. -# Prefer the bin that already holds the most previously-unnamed collections, so -# `default` keeps meaning "the misc tail" rather than silently owning a headline -# collection. Never pick a bin holding a folder-sharded unit. -was_default = { - k[5:] if k.startswith("TEST-") else k - for g in ("default", "default-split") for k in raw.get(g, {}) -} -# Units that must stay explicitly listed: folder-sharded ones (config drives the -# split) and ContentTypeResourceTests (#36915 deliberately placed it by name). -PINNED = ("GraphQLTests@", "ContentTypeResourceTests") -default_idx = max( - (i for i, b in enumerate(bins) - if not any(m.startswith(PINNED) for m in b)), - key=lambda i: sum(1 for m in bins[i] if m in was_default), -) -print(f"\nbin {default_idx} ({names[default_idx]}) left UNLISTED -> becomes the `default` shard") - -out = [] -for i, (n, b) in enumerate(zip(names, bins)): - if i == default_idx: - continue - entry = {"name": n} - colls, folders = [], None - for m in sorted(b, key=lambda m: -units[m]): - if m.startswith("GraphQLTests@"): - colls.append("GraphQLTests") - folders = {"GraphQLTests": [gsplit["setup"]] + - (gsplit["a"] if m.endswith("@a") else gsplit["b"])} - else: - colls.append(m) - entry["collections"] = colls - if folders: - entry["folders"] = folders - out.append(entry) - -shard_names = [e["name"] for e in out] + ["default"] -json.dump({"groups": out, "shards": shard_names, - "default_members": sorted(bins[default_idx])}, - open(f"{HERE}/postman_groups.json", "w"), indent=2) -print(f"wrote postman_groups.json: {len(out)} listed groups + default = {len(shard_names)} shards") diff --git a/.github/scripts/test-balance/pack_suites.py b/.github/scripts/test-balance/pack_suites.py deleted file mode 100644 index cb772237d915..000000000000 --- a/.github/scripts/test-balance/pack_suites.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 -"""Bin-pack the 557 MainSuite integration classes into N balanced suites. - -Balances on measured per-class test time (stable run-to-run: 115.2m vs 114.8m -across two runs). Job wall time is NOT used - it is dominated by runner variance. - -Front-block classes (the "run FIRST on purpose" full-scan tests from #36911) are -packed normally but emitted at the head of whichever suite they land in. -""" -import os -import json, os, re, collections - -HERE = os.environ.get("BALANCE_WORKDIR", os.path.dirname(os.path.abspath(__file__))) -WT = os.environ.get("REPO_ROOT", os.getcwd()) -SRC = f"{WT}/dotcms-integration/src/test/java/com/dotcms" -NBINS = 7 -NAMES = ["MainSuite1a", "MainSuite1b", "MainSuite2a", "MainSuite2b", - "MainSuite3a", "MainSuite3b", "MainSuite4a"] - -parsed = json.load(open(f"{HERE}/suites_parsed.json")) -times = json.load(open(f"{HERE}/post_classes.json")) # fqn -> seconds - -# ---- build the pool ------------------------------------------------------- -pool, front = [], set() -for suite, v in parsed.items(): - front |= set(v["front"]) - pool += v["entries"] -assert len(pool) == len(set(pool)), "duplicate classes in pool" - -by_simple = collections.defaultdict(list) -for k in times: - by_simple[k.split(".")[-1]].append(k) - - -def cost(fqn): - if fqn in times: - return times[fqn] - cands = by_simple.get(fqn.split(".")[-1]) # package moved / inner class - return times[cands[0]] if cands else 0.0 - - -missing = [c for c in pool if cost(c) == 0.0] -print(f"pool={len(pool)} front={len(front)} zero-cost={len(missing)}") - -# ---- LPT bin-pack --------------------------------------------------------- -bins = [[] for _ in range(NBINS)] -load = [0.0] * NBINS -for c in sorted(pool, key=cost, reverse=True): - i = load.index(min(load)) - bins[i].append(c) - load[i] += cost(c) - -# order within each bin: front-block first, then descending cost -for b in bins: - b.sort(key=lambda c: (c not in front, -cost(c))) - -print(f"\n{'suite':<14}{'classes':>9}{'test-time':>11}{'front':>7}") -for n, b, l in zip(NAMES, bins, load): - print(f"{n:<14}{len(b):>9}{l/60:>10.1f}m{sum(1 for c in b if c in front):>7}") -print(f"\ntotal {sum(load)/60:.1f}m | perfect {sum(load)/60/NBINS:.1f}m " - f"| max {max(load)/60:.1f}m | spread {(max(load)-min(load))/60:.1f}m") - -# ---- emit ----------------------------------------------------------------- -FRONT_NOTE = """ - // Data-scanning tests run FIRST on purpose. - // Integration tests accumulate content and never clean up, so anything - // that walks the whole dataset (executeUpgrade, findAll*) costs - // O(all content created so far). Scheduled late these pay for every - // preceding test's leftovers. Keep new full-scan tests in this block. -""" - -TMPL = """package com.dotcms; - -import com.dotcms.junit.MainBaseSuite; -import org.junit.runner.RunWith; -import org.junit.runners.Suite.SuiteClasses; - -/** - * Integration test suite shard {idx} of {n}. - * - * Shards are balanced on measured per-class test time so the slowest shard - * bounds the CI critical path as tightly as possible. When adding a test, - * put it in the shard with the lowest total time rather than appending here - * by habit - see .github/test-matrix.yml for the shard list. - * - * Classes are fully qualified so that rebalancing does not churn imports. - */ -@RunWith(MainBaseSuite.class) -@SuiteClasses({{ -{body} -}}) -public class {cls} {{ - -}} -""" - - -def emit(cls, idx, classes): - lines, wrote_note = [], False - for c in classes: - if c in front and not wrote_note: - lines.append(FRONT_NOTE.rstrip("\n")) - wrote_note = True - if c not in front and wrote_note: - lines.append("") - wrote_note = None # close the block once - lines.append(f" {c}.class,") - body = "\n".join(lines).rstrip(",") - # trailing comma on the final entry is legal in Java annotations, keep it simple - body = "\n".join(lines) - if body.rstrip().endswith(","): - body = body.rstrip()[:-1] - return TMPL.format(cls=cls, idx=idx, n=NBINS, body=body) - - -for i, (n, b) in enumerate(zip(NAMES, bins), 1): - open(f"{SRC}/{n}.java", "w").write(emit(n, i, b)) - print(f"wrote {n}.java") - -json.dump({n: b for n, b in zip(NAMES, bins)}, open(f"{HERE}/packed.json", "w"), indent=1) diff --git a/.github/scripts/test-balance/parse_suites.py b/.github/scripts/test-balance/parse_suites.py deleted file mode 100644 index ba3774e3af96..000000000000 --- a/.github/scripts/test-balance/parse_suites.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -"""Parse MainSuite*.java @SuiteClasses lists -> {suite: {'entries': [fqn], 'front': [fqn]}} - -Front block = the "run FIRST on purpose" cluster added by #36911; those classes do -full-DB scans and must stay at the head of whatever suite they end up in. -""" -import os, re, os, json, sys - -WT = os.environ.get("REPO_ROOT", os.getcwd()) -SRC = f"{WT}/dotcms-integration/src/test/java/com/dotcms" -SUITES = ["MainSuite1a", "MainSuite1b", "MainSuite2a", "MainSuite2b", "MainSuite3a"] -FRONT_MARK = re.compile(r"run FIRST on purpose") - - -def parse(suite): - s = open(f"{SRC}/{suite}.java").read() - imports = dict( - (m.split(".")[-1], m) - for m in re.findall(r"^import\s+([\w.]+);", s, re.M) - if not m.startswith("org.junit") - ) - m = re.search(r"@(?:Suite\.)?SuiteClasses\(\{", s) - body = s[m.end(): s.index("})", m.end())] - - entries, front = [], [] - in_front = False # inside the "run FIRST" cluster - front_done = False - for raw in body.split("\n"): - line = raw.strip() - if not line: - if in_front: # blank line closes the front cluster - in_front, front_done = False, True - continue - if line.startswith(("//", "/*", "*")): - if not front_done and FRONT_MARK.search(line): - in_front = True - continue - mm = re.match(r"([\w.]+)\.class\s*,?", line) - if not mm: - continue - name = mm.group(1) - fqn = name if "." in name else imports.get(name) - if fqn is None: - print(f" !! UNRESOLVED {suite}: {name}", file=sys.stderr) - sys.exit(1) - entries.append(fqn) - if in_front: - front.append(fqn) - return {"entries": entries, "front": front} - - -if __name__ == "__main__": - out = {} - for s in SUITES: - out[s] = parse(s) - print(f"{s:<14} {len(out[s]['entries']):>4} classes, front block = {len(out[s]['front'])}") - for f in out[s]["front"]: - print(f" FRONT {f.split('.')[-1]}") - allc = [c for v in out.values() for c in v["entries"]] - print(f"\ntotal {len(allc)}, unique {len(set(allc))}") - dupes = sorted(c for c in set(allc) if allc.count(c) > 1) - print("DUPLICATES:", dupes if dupes else "none") - json.dump(out, open(os.path.dirname(__file__) + "/suites_parsed.json", "w"), indent=1) diff --git a/.github/test-matrix.yml b/.github/test-matrix.yml index e6fc357314a8..d9fe80aa8168 100644 --- a/.github/test-matrix.yml +++ b/.github/test-matrix.yml @@ -62,10 +62,6 @@ test_types: verify -Dit.test.forkcount=1 -pl :dotcms-integration -Dcoreit.test.skip=false suites: - # Shards are balanced on measured per-class test time (~18.4m each), not on - # class count - class count is a poor proxy (one shard was 298 classes/31m, - # another 52 classes/14m). Rebalance with the same measurement when shard - # times drift apart; see scripts referenced in the PR that introduced this. - name: "Integration Tests - MainSuite 1a" test_class: "MainSuite1a" stage_name: "IT Tests MainSuite 1a" @@ -81,12 +77,6 @@ test_types: - name: "Integration Tests - MainSuite 3a" test_class: "MainSuite3a" stage_name: "IT Tests MainSuite 3a" - - name: "Integration Tests - MainSuite 3b" - test_class: "MainSuite3b" - stage_name: "IT Tests MainSuite 3b" - - name: "Integration Tests - MainSuite 4a" - test_class: "MainSuite4a" - stage_name: "IT Tests MainSuite 4a" - name: "Integration Tests - Junit5 Suite 1" test_class: "Junit5Suite1" stage_name: "IT Tests Junit5Suite1" @@ -111,34 +101,36 @@ test_types: base_maven_args: >- verify -pl :dotcms-postman -Dpostman.test.skip=false - # Groups are balanced on measured newman time (~16.4m each) and defined in - # dotcms-postman/config.json. Every shard pays ~9.4m of fixed cost (artifact - # download, docker load, dotCMS boot), so a handful of tiny groups is pure - # waste - keep groups few and evenly sized rather than thematically pure. - # - # `default` is special: index.js computes it as every collection on disk NOT - # listed in config.json, so it is the safety net that runs newly added - # collections. It must always have a shard here. suites: - - name: "Postman Tests - Content" - collection: "content" - - name: "Postman Tests - ContentType" - collection: "contenttype" - # GraphQLTests is one 25.6m collection - too big for a single shard and the - # binding constraint on the whole Postman tail. config.json splits it by - # top-level folder instead of splitting the 518KB collection file. - - name: "Postman Tests - GraphQL A" - collection: "graphql-a" - - name: "Postman Tests - GraphQL B" - collection: "graphql-b" - - name: "Postman Tests - Pages" - collection: "pages" - - name: "Postman Tests - Site" - collection: "site" + # AI and ML related tests + - name: "Postman Tests - AI" + collection: "ai" + + # Content management tests + - name: "Postman Tests - Category Content" + collection: "category-content" + - name: "Postman Tests - Container" + collection: "container" + - name: "Postman Tests - Page" + collection: "page" - name: "Postman Tests - Template" collection: "template" + + # Feature-specific tests + - name: "Postman Tests - Experiment" + collection: "experiment" + - name: "Postman Tests - GraphQL" + collection: "graphql" - name: "Postman Tests - Workflow" collection: "workflow" + + # Push/Publish tests + - name: "Postman Tests - PP" + collection: "pp" + + # Default test suites + - name: "Postman Tests - Default Split" + collection: "default-split" - name: "Postman Tests - Default" collection: "default" diff --git a/.github/workflows/cicd_comp_test-phase.yml b/.github/workflows/cicd_comp_test-phase.yml index 7b61431c72a3..68c1dfd4bcac 100644 --- a/.github/workflows/cicd_comp_test-phase.yml +++ b/.github/workflows/cicd_comp_test-phase.yml @@ -232,16 +232,7 @@ jobs: # a comma-list subset like '1,2', or 'all') run every suite/phase to completion so # failures are attributable per phase. Derived from "phase off" rather than an # exact-match list so comma-list subsets are covered too. - # !!! TEMPORARY - REVERT BEFORE MERGING THIS PR !!! - # Restore to: - # fail-fast: ${{ inputs.opensearch_phase == '' || inputs.opensearch_phase == 'none' || inputs.opensearch_phase == '0' }} - # - # This PR re-shards the integration suites, which exposes tests that were - # only passing because of which suite-mates ran before them. With fast-fail - # on, the first broken shard cancels the other 25 jobs, so each ~40 minute - # run reveals exactly one shard's worth of coupling. Off, a single run - # surfaces all of them at once. - fail-fast: false + fail-fast: ${{ inputs.opensearch_phase == '' || inputs.opensearch_phase == 'none' || inputs.opensearch_phase == '0' }} matrix: ${{ fromJSON(needs.setup-matrix.outputs.matrix) }} steps: diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite1a.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite1a.java index 04d266e1c577..2d7da2cfafc5 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite1a.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite1a.java @@ -1,19 +1,45 @@ package com.dotcms; +import com.dotcms.ai.workflow.OpenAIGenerateImageActionletTest; +import com.dotcms.analytics.track.RequestMatcherTest; +import com.dotcms.contenttype.business.SiteAndFolderResolverImplTest; +import com.dotcms.enterprise.publishing.remote.PushPublishBundleGeneratorTest; +import com.dotcms.enterprise.publishing.remote.bundler.DependencyBundlerTest; +import com.dotcms.enterprise.publishing.remote.bundler.RuleBundlerTest; +import com.dotcms.enterprise.publishing.staticpublishing.StaticPublisherIntegrationTest; +import com.dotcms.enterprise.rules.RulesAPIImplIntegrationTest; +import com.dotcms.experiments.business.ExperimentAPIImpIntegrationTest; +import com.dotcms.experiments.business.ExperimentUrlPatternCalculatorIntegrationTest; +import com.dotcms.experiments.business.web.ExperimentWebAPIImplIntegrationTest; +import com.dotcms.graphql.DotGraphQLHttpServletTest; +import com.dotcms.integritycheckers.ContentFileAssetIntegrityCheckerTest; +import com.dotcms.integritycheckers.ContentPageIntegrityCheckerTest; +import com.dotcms.integritycheckers.FolderIntegrityCheckerTest; +import com.dotcms.integritycheckers.HostIntegrityCheckerTest; import com.dotcms.junit.MainBaseSuite; +import com.dotcms.publisher.bundle.business.BundleFactoryImplTest; +import com.dotcms.publisher.business.PublishQueueElementTransformerTest; +import com.dotcms.publisher.util.DependencyModDateUtilTest; +import com.dotcms.publishing.job.SiteSearchJobImplTest; +import com.dotcms.rendering.js.JsEngineTest; +import com.dotcms.rendering.velocity.viewtools.XsltToolTest; +import com.dotcms.storage.FileMetadataAPITest; +import com.dotcms.uuid.shorty.LegacyShortyIdApiTest; +import com.dotmarketing.cache.FolderCacheImplIntegrationTest; +import com.dotmarketing.portlets.contentlet.business.HostFactoryImplTest; +import com.dotmarketing.portlets.contentlet.business.web.ContentletWebAPIImplIntegrationTest; +import com.dotmarketing.portlets.workflows.actionlet.EmailActionletTest; +import com.dotmarketing.quartz.job.StartEndScheduledExperimentsJobTest; +import com.dotmarketing.startup.runonce.Task220825CreateVariantFieldTest; +import com.dotmarketing.startup.runonce.Task221007AddVariantIntoPrimaryKeyTest; +import com.dotmarketing.startup.runonce.Task240306MigrateLegacyLanguageVariablesTest; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; -/** - * Integration test suite shard 1 of 7. - * - * Shards are balanced on measured per-class test time so the slowest shard - * bounds the CI critical path as tightly as possible. When adding a test, - * put it in the shard with the lowest total time rather than appending here - * by habit - see .github/test-matrix.yml for the shard list. - * - * Classes are fully qualified so that rebalancing does not churn imports. - */ +/* grep -l -r "@Test" dotCMS/src/integration-test */ +/* ./gradlew integrationTest -Dtest.single=com.dotcms.MainSuite */ + + @RunWith(MainBaseSuite.class) @SuiteClasses({ @@ -22,86 +48,85 @@ // that walks the whole dataset (executeUpgrade, findAll*) costs // O(all content created so far). Scheduled late these pay for every // preceding test's leftovers. Keep new full-scan tests in this block. - com.dotmarketing.startup.runonce.Task240306MigrateLegacyLanguageVariablesTest.class, - com.dotmarketing.factories.MultiTreeAPITest.class, + Task240306MigrateLegacyLanguageVariablesTest.class, + com.dotmarketing.portlets.templates.business.TemplateAPITest.class, + com.dotmarketing.portlets.containers.business.ContainerAPIImplTest.class, - com.dotcms.vanityurl.business.VanityUrlAPITest.class, - com.dotcms.enterprise.publishing.remote.bundler.DependencyBundlerTest.class, - com.dotmarketing.filters.FiltersTest.class, - com.dotcms.experiments.business.web.ExperimentWebAPIImplIntegrationTest.class, - com.dotmarketing.business.PermissionAPITest.class, - com.dotcms.rest.api.v1.page.PageRenderSourcesResourceTest.class, - com.dotcms.rest.api.v1.folder.FolderResourceTest.class, - com.dotcms.enterprise.publishing.staticpublishing.StaticPublisherIntegrationTest.class, - com.dotcms.security.apps.AppsAPIImplTest.class, - com.dotcms.rendering.velocity.viewtools.content.ContentMapTest.class, - com.dotcms.contenttype.business.FieldAPITest.class, - com.dotcms.graphql.business.GraphqlAPITest.class, - com.dotmarketing.util.contentlet.pagination.PaginatedContentletsIntegrationTest.class, - com.dotcms.rest.api.v1.publishing.PublishingResourceIntegrationTest.class, - com.dotmarketing.servlets.ShortyServletAndTitleImageTest.class, - com.dotcms.rendering.velocity.viewtools.ContainerWebAPIIntegrationTest.class, - com.dotcms.rendering.velocity.viewtools.FileToolTest.class, - com.dotmarketing.portlets.structure.factories.FieldFactoryTest.class, - com.dotcms.analytics.track.collectors.WebEventsCollectorServiceImplTest.class, - com.dotmarketing.portlets.workflows.actionlet.EmailActionletTest.class, - com.dotcms.rendering.velocity.viewtools.WorkflowToolTest.class, - com.liferay.portal.ejb.UserUtilTest.class, - com.dotmarketing.quartz.job.StartEndScheduledExperimentsJobTest.class, + StartEndScheduledExperimentsJobTest.class, + RulesAPIImplIntegrationTest.class, + ExperimentAPIImpIntegrationTest.class, + ExperimentWebAPIImplIntegrationTest.class, + ContentletWebAPIImplIntegrationTest.class, // moved to top because of failures on GHA + DependencyBundlerTest.class, // moved to top because of failures on GHA + SiteAndFolderResolverImplTest.class, //Moved up to avoid conflicts with CT deletion + FolderCacheImplIntegrationTest.class, + StaticPublisherIntegrationTest.class, com.dotcms.publishing.PublisherAPIImplTest.class, - com.dotcms.keyvalue.busines.KeyValueAPIImplTest.class, - com.dotcms.rest.api.v1.drive.ContentDriveFieldFilterTest.class, - com.dotcms.telemetry.collectors.experiment.CountVariantsInAllDraftExperimentsMetricTypeTest.class, - com.dotcms.publisher.business.PublishAuditAPITest.class, - com.dotcms.util.pagination.ContainerPaginatorTest.class, - com.dotcms.ai.client.AIProxyClientTest.class, - com.dotcms.rendering.velocity.viewtools.navigation.NavToolCacheTest.class, - com.dotmarketing.portlets.contentlet.transform.BinaryToMapTransformerTest.class, - com.dotcms.ai.workflow.OpenAIGenerateImageActionletTest.class, - com.dotcms.timemachine.business.TimeMachineAPITest.class, - com.dotcms.storage.StoragePersistenceAPITest.class, - com.dotcms.rest.api.v2.contenttype.FieldResourceTest.class, - com.dotmarketing.db.HibernateUtilTest.class, - com.dotmarketing.quartz.job.EncryptPlainPasswordsJobTest.class, - com.dotmarketing.business.helper.PermissionHelperTest.class, - com.dotmarketing.startup.runonce.Task230426AlterVarcharLengthOfLockedByColTest.class, - com.dotmarketing.startup.runonce.Task201014UpdateColumnsValuesInIdentifierTableTest.class, - com.dotmarketing.startup.runonce.Task250826AddIndexesToUniqueFieldsTableTest.class, - com.dotmarketing.startup.runonce.Task210506UpdateStorageTableTest.class, - com.dotmarketing.business.RoleAPITest.class, - com.dotcms.publisher.assets.business.PushedAssetsAPITest.class, - com.dotmarketing.business.CommitListenerCacheWrapperTest.class, - com.dotmarketing.portlets.workflows.actionlet.CopyActionletTest.class, + SiteSearchJobImplTest.class, + XsltToolTest.class, + PushPublishBundleGeneratorTest.class, + LegacyShortyIdApiTest.class, + RuleBundlerTest.class, + org.apache.velocity.runtime.parser.node.SimpleNodeTest.class, + com.liferay.portal.ejb.UserLocalManagerTest.class, + com.liferay.portal.ejb.UserUtilTest.class, + com.liferay.util.LocaleUtilTest.class, + com.dotcms.languagevariable.business.LanguageVariableAPITest.class, + com.dotcms.publishing.PublisherAPITest.class, + com.dotcms.publishing.remote.RemoteReceiverLanguageResolutionTest.class, + com.dotcms.cluster.business.ServerAPIImplTest.class, + com.dotcms.cache.KeyValueCacheImplTest.class, + com.dotcms.enterprise.publishing.remote.handler.RuleBundlerHandlerTest.class, com.dotcms.enterprise.publishing.remote.CategoryBundlerHandlerTest.class, - com.dotcms.contenttype.business.DotAssetBaseTypeToContentTypeStrategyImplTest.class, - com.dotcms.util.TimeMachineUtilTest.class, + com.dotcms.enterprise.publishing.remote.HostBundlerHandlerTest.class, + com.dotcms.enterprise.priv.ESSearchProxyTest.class, + com.dotcms.util.pagination.ContentTypesPaginatorTest.class, + com.dotcms.util.marshal.MarshalUtilsIntegrationTest.class, + com.dotcms.util.RelationshipUtilTest.class, + com.dotcms.util.ImportUtilTest.class, + com.dotcms.publisher.business.PublisherAPIImplTest.class, + PublishQueueElementTransformerTest.class, + com.dotmarketing.util.PageModeTest.class, + com.dotmarketing.business.web.UserWebAPIImplTest.class, + com.dotcms.auth.providers.jwt.JsonWebTokenUtilsIntegrationTest.class, + com.dotcms.auth.providers.jwt.factories.ApiTokenAPITest.class, + com.dotcms.auth.providers.jwt.services.JsonWebTokenServiceIntegrationTest.class, + DependencyModDateUtilTest.class, + com.dotcms.publisher.business.PublisherTest.class, + com.dotcms.enterprise.publishing.PublishDateUpdaterIntegrationTest.class, + com.dotcms.publisher.endpoint.bean.PublishingEndPointTest.class, + com.dotcms.publisher.endpoint.business.PublishingEndPointAPITest.class, + com.dotcms.publisher.endpoint.business.PublishingEndPointFactoryImplTest.class, + com.dotcms.publisher.assets.business.PushedAssetsAPITest.class, + com.dotcms.notification.business.NotificationAPITest.class, + com.dotcms.business.LocalTransactionAndCloseDBIfOpenedFactoryTest.class, + com.dotcms.business.bytebuddy.ByteBuddyAdviceWeavingTest.class, + FolderIntegrityCheckerTest.class, + HostFactoryImplTest.class, + BundleFactoryImplTest.class, + ExperimentUrlPatternCalculatorIntegrationTest.class, + JsEngineTest.class, + EmailActionletTest.class, + OpenAIGenerateImageActionletTest.class, + RequestMatcherTest.class, + com.dotmarketing.portlets.rules.conditionlet.ConditionletOSGIFTest.class, com.dotmarketing.portlets.rules.conditionlet.CurrentSessionLanguageConditionletTest.class, - com.dotmarketing.quartz.job.PruneTimeMachineBackupJobTest.class, - com.dotcms.saml.SamlConfigurationServiceTest.class, - com.dotcms.graphql.datafetcher.FolderCollectionDataFetcherTest.class, - com.dotmarketing.startup.runonce.Task241013RemoveFullPathLcColumnFromIdentifierTest.class, - com.dotmarketing.startup.runonce.Task210527DropReviewFieldsFromContentletTableTest.class, - com.dotcms.graphql.DotGraphQLHttpServletTest.class, - com.dotmarketing.util.ITConfigTest.class, - com.dotmarketing.startup.runonce.Task230630CreateRunningIdsExperimentFieldIntegrationTest.class, - com.dotcms.business.SystemTableFactoryTest.class, - com.dotmarketing.startup.runonce.Task210802UpdateStructureTableTest.class, - com.dotmarketing.business.web.LanguageWebApiTest.class, - com.dotcms.analytics.track.collectors.BasicProfileCollectorTest.class, - com.dotmarketing.startup.runonce.Task220824CreateDefaultVariantTest.class, - com.dotmarketing.portlets.folders.model.FolderTest.class, - com.dotmarketing.startup.runonce.Task260615AlterClusterIdLengthTest.class, - com.dotmarketing.startup.runonce.Task220214AddOwnerAndIDateToFolderTableTest.class, - com.dotmarketing.startup.runonce.Task230707CreateSystemTableTest.class, - com.dotcms.business.interceptor.InterceptorHandlerTest.class, - com.dotcms.content.business.ObjectMapperTest.class, - com.dotmarketing.startup.runonce.Task05370AddAppsPortletToLayoutTest.class, - com.dotmarketing.startup.runonce.Task210520UpdateAnonymousEmailTest.class, - com.dotcms.storage.Chainable404StorageCacheTest.class, + com.dotmarketing.portlets.rules.conditionlet.NumberOfTimesPreviouslyVisitedConditionletTest.class, + com.dotmarketing.portlets.rules.conditionlet.UsersBrowserLanguageConditionletTest.class, + com.dotmarketing.portlets.rules.conditionlet.UsersSiteVisitsConditionletTest.class, com.dotmarketing.portlets.rules.conditionlet.VisitorOperatingSystemConditionletTest.class, - com.dotcms.security.apps.AppsCacheImplTest.class, - com.dotcms.api.web.HttpServletRequestImpersonatorTest.class + com.dotmarketing.portlets.rules.conditionlet.VisitedUrlConditionletTest.class, + com.dotmarketing.portlets.rules.business.RulesCacheFTest.class, + com.dotmarketing.portlets.folders.business.FolderAPITest.class, + com.dotmarketing.portlets.containers.business.ContainerAPITest.class, + com.dotmarketing.portlets.containers.business.FileAssetContainerUtilTest.class, + com.dotmarketing.portlets.htmlpages.business.HTMLPageAPITest.class, + com.dotmarketing.portlets.structure.factories.StructureFactoryTest.class, + com.dotmarketing.portlets.structure.factories.FieldFactoryTest.class, + com.dotmarketing.portlets.structure.model.ContentletRelationshipsTest.class, + com.dotmarketing.portlets.structure.transform.ContentletRelationshipsTransformerTest.class, }) + public class MainSuite1a { } diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite1b.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite1b.java index 720cc0c4eb32..0f5cc787de6a 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite1b.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite1b.java @@ -1,107 +1,105 @@ package com.dotcms; +import com.dotcms.graphql.DotGraphQLHttpServletTest; import com.dotcms.junit.MainBaseSuite; +import com.dotcms.storage.Chainable404StorageCacheTest; +import com.dotcms.storage.FileStorageAPITest; +import com.dotmarketing.common.db.DotConnectTest; +import com.dotmarketing.quartz.QuartzUtilsTest; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; -/** - * Integration test suite shard 2 of 7. - * - * Shards are balanced on measured per-class test time so the slowest shard - * bounds the CI critical path as tightly as possible. When adding a test, - * put it in the shard with the lowest total time rather than appending here - * by habit - see .github/test-matrix.yml for the shard list. - * - * Classes are fully qualified so that rebalancing does not churn imports. - */ -@RunWith(MainBaseSuite.class) -@SuiteClasses({ +/* grep -l -r "@Test" dotCMS/src/integration-test */ +/* ./gradlew integrationTest -Dtest.single=com.dotcms.MainSuite */ - // Data-scanning tests run FIRST on purpose. - // Integration tests accumulate content and never clean up, so anything - // that walks the whole dataset (executeUpgrade, findAll*) costs - // O(all content created so far). Scheduled late these pay for every - // preceding test's leftovers. Keep new full-scan tests in this block. - com.dotmarketing.common.reindex.ReindexAPITest.class, - com.dotcms.rendering.velocity.viewtools.content.util.ContentUtilsTest.class, - com.dotcms.content.elasticsearch.business.ESContentFactoryImplTest.class, - com.dotcms.enterprise.rules.RulesAPIImplIntegrationTest.class, - com.dotcms.publishing.job.SiteSearchJobImplTest.class, - com.dotcms.contenttype.business.uniquefields.extratable.UniqueFieldDataBaseUtilTest.class, - com.dotmarketing.portlets.contentlet.business.HostFactoryImplTest.class, - com.dotcms.graphql.datafetcher.page.ContentMapDataFetcherTest.class, - com.dotcms.publisher.util.DependencyManagerTest.class, - com.dotcms.rest.api.v1.asset.WebAssetHelperIntegrationTest.class, - com.dotcms.enterprise.publishing.remote.StaticPushPublishBundleGeneratorTest.class, - com.dotmarketing.portlets.contentlet.business.ContentletCheckInTest.class, +@RunWith(MainBaseSuite.class) +@SuiteClasses({ + com.dotcms.keyvalue.busines.KeyValueAPIImplTest.class, + com.dotcms.keyvalue.business.KeyValueAPITest.class, + com.dotcms.tika.TikaUtilsTest.class, + com.dotcms.visitor.filter.logger.VisitorLoggerTest.class, + com.dotcms.visitor.filter.characteristics.VisitorCharacterTest.class, + com.dotcms.graphql.business.GraphqlAPITest.class, + com.dotcms.contenttype.test.ContentTypeTest.class, + com.dotcms.contenttype.test.DeleteFieldJobTest.class, + com.dotcms.content.elasticsearch.business.ESSiteSearchAPITest.class, + com.dotcms.content.elasticsearch.business.ESMappingAPITest.class, + com.dotcms.content.elasticsearch.business.ContentletIndexAPIImplTest.class, + com.dotcms.contenttype.test.ContentTypeAPIImplTest.class, + com.dotcms.contenttype.test.ContentTypeBuilderTest.class, + com.dotcms.contenttype.test.ContentTypeFactoryImplTest.class, + com.dotcms.contenttype.test.ContentTypeImportExportTest.class, + com.dotcms.contenttype.test.FieldFactoryImplTest.class, + com.dotcms.contenttype.test.JsonContentTypeTransformerTest.class, + com.dotcms.contenttype.test.FieldBuilderTest.class, + com.dotcms.contenttype.test.KeyValueFieldUtilTest.class, + com.dotcms.contenttype.test.ContentTypeResourceTest.class, com.dotcms.contenttype.business.RelationshipAPITest.class, - com.dotcms.rest.api.v1.content.ContentResourceIntegrationTest.class, - com.dotcms.rest.api.v1.maintenance.MaintenanceResourceIntegrationTest.class, - com.dotcms.rest.elasticsearch.ESContentResourcePortletTest.class, - com.dotcms.experiments.business.RootIndexRegexUrlPatterStrategyIntegrationTest.class, - com.dotmarketing.portlets.contentlet.util.ContentletUtilTest.class, - com.dotcms.util.RelationshipUtilTest.class, - com.dotcms.auth.providers.jwt.factories.ApiTokenAPITest.class, - com.dotcms.enterprise.publishing.remote.HostBundlerHandlerTest.class, - com.dotmarketing.portlets.templates.business.TemplateFactoryImplTest.class, - com.dotmarketing.portlets.browser.ajax.BrowserAjaxTest.class, + com.dotcms.contenttype.business.FieldAPITest.class, com.dotcms.contenttype.business.RelationshipFactoryImplTest.class, - com.dotcms.rest.api.v1.container.ContainerResourceHostResolutionIT.class, - com.dotcms.contenttype.test.DotAssetAPITest.class, - com.dotmarketing.startup.runonce.Task05200WorkflowTaskUniqueKeyTest.class, - com.dotmarketing.portlets.contentlet.transform.WidgetViewStrategyTest.class, - com.dotcms.telemetry.collectors.experiment.CountPagesWithDraftExperimentsMetricTypeTest.class, - com.dotcms.auth.providers.saml.v1.SAMLHelperTest.class, - com.dotcms.analytics.track.collectors.SyncVanitiesCollectorTest.class, - com.dotcms.analytics.track.collectors.AsyncVanitiesCollectorTest.class, - com.dotmarketing.business.LayoutAPITest.class, - com.dotmarketing.portlets.workflows.actionlet.SaveContentActionletTest.class, - com.dotmarketing.business.PermissionBitFactoryImplTest.class, - com.dotcms.rest.api.v1.contenttype.ContentTypeResourceUpdateMetadataTest.class, - com.dotcms.analytics.track.collectors.FilesCollectorTest.class, - com.dotmarketing.startup.runonce.Task210901UpdateDateTimezonesTest.class, - com.dotcms.rest.api.v1.drive.ContentDriveWorkflowArchiveStepTest.class, - com.dotcms.ai.workflow.OpenAIContentPromptActionletTest.class, - com.dotmarketing.portlets.workflows.actionlet.MoveContentActionletTest.class, - com.dotcms.uuid.shorty.LegacyShortyIdApiTest.class, - com.dotcms.publisher.business.PublishQueueElementTransformerTest.class, - com.dotmarketing.startup.runonce.Task05210CreateDefaultDotAssetTest.class, - com.dotcms.security.multipart.ContentDispositionFileNameParserTest.class, - com.dotcms.rest.api.v1.pushpublish.PushPublishFilterResourceTest.class, - com.dotmarketing.startup.runonce.Task05170DefineFrontEndAndBackEndRolesTest.class, + com.dotcms.contenttype.model.field.layout.FieldLayoutColumnSerializerTest.class, + com.dotcms.contenttype.model.field.layout.FieldLayoutSerializerTest.class, + com.dotcms.contenttype.model.field.layout.FieldLayoutRowSerializerTest.class, + com.dotcms.contenttype.model.field.layout.FieldLayoutTest.class, + com.dotcms.workflow.helper.TestSystemActionMappingsHandlerMerger.class, + com.dotcms.concurrent.lock.DotKeyLockManagerTest.class, + com.dotcms.rendering.velocity.ASTMethodTest.class, com.dotcms.rendering.velocity.VelocityMacroCacheTest.class, - com.dotmarketing.util.PageModeTest.class, - com.dotmarketing.servlets.ajax.AjaxDirectorServletIntegrationTest.class, + com.dotcms.rendering.velocity.VelocityUtilTest.class, + com.dotcms.rendering.velocity.viewtools.navigation.NavToolTest.class, + com.dotcms.rendering.velocity.viewtools.navigation.NavToolCacheTest.class, + com.dotcms.rendering.velocity.viewtools.content.ContentMapTest.class, + com.dotcms.rendering.velocity.viewtools.content.ContentToolTest.class, + com.dotcms.rendering.velocity.viewtools.ContentSearchToolTest.class, + com.dotmarketing.sitesearch.viewtool.SiteSearchWebAPITest.class, + com.dotcms.rendering.velocity.viewtools.WorkflowToolTest.class, + com.dotcms.rendering.velocity.viewtools.WebsiteToolTest.class, + com.dotcms.rendering.velocity.viewtools.LanguageWebAPITest.class, + com.dotcms.rendering.velocity.viewtools.ContainerWebAPIIntegrationTest.class, + com.dotcms.rendering.velocity.services.VelocityResourceKeyTest.class, + com.dotcms.rendering.velocity.services.HTMLPageAssetRenderedTest.class, + com.dotcms.uuid.shorty.ShortyIdApiTest.class, + DotGraphQLHttpServletTest.class, + com.dotcms.graphql.datafetcher.page.VanityURLFetcherTest.class, + com.dotcms.graphql.datafetcher.page.RunningExperimentFetcherTest.class, + com.dotcms.graphql.datafetcher.CategoryFieldDataFetcherTest.class, + com.dotcms.graphql.datafetcher.FolderCollectionDataFetcherTest.class, + com.dotcms.rest.TagResourceIntegrationTest.class, + com.dotcms.rest.api.v2.tags.TagResourceIntegrationTest.class, + com.dotcms.rest.MapToContentletPopulatorTest.class, + com.dotcms.rest.WebResourceIntegrationTest.class, com.dotcms.rest.api.v1.company.CompanyResourceIntegrationTest.class, - com.dotcms.enterprise.publishing.remote.handler.ContentWorkflowHandlerTest.class, - com.dotmarketing.quartz.job.PopulateContentletAsJSONJobTest.class, - com.dotmarketing.startup.runonce.Task05030UpdateSystemContentTypesHostTest.class, - com.dotmarketing.quartz.job.IntegrityDataGenerationJobTest.class, - com.dotcms.cost.RequestCostReportTest.class, - com.dotcms.rendering.velocity.viewtools.XmlToolTest.class, - com.dotmarketing.startup.runonce.Task251212AddVersionColumnIndicesTableTest.class, - com.dotcms.business.SystemAPITest.class, - com.dotmarketing.startup.runonce.Task04335CreateSystemWorkflowTest.class, - com.dotcms.dotpubsub.RedisPubSubImplTest.class, - com.dotcms.tika.TikaUtilsTest.class, - com.dotcms.enterprise.publishing.remote.bundler.LinkBundlerTest.class, - com.dotcms.contenttype.test.KeyValueFieldUtilTest.class, - com.dotmarketing.business.IdentifierCacheImplTest.class, - com.dotcms.publisher.endpoint.business.PublishingEndPointFactoryImplTest.class, - com.dotmarketing.startup.runonce.Task240102AlterVarcharLengthOfRelationTypeTest.class, - com.dotmarketing.startup.runonce.Task05225RemoveLoadRecordsToIndexTest.class, - com.dotmarketing.startup.runonce.Task210510UpdateStorageTableDropMetadataColumnTest.class, - com.dotmarketing.startup.runonce.Task220928AddLookbackWindowColumnToExperimentTest.class, - com.dotmarketing.startup.runonce.Task05305AddPushPublishFilterColumnTest.class, - com.dotmarketing.portlets.rules.conditionlet.NumberOfTimesPreviouslyVisitedConditionletTest.class, - com.dotmarketing.portlets.workflows.model.WorkflowSearcherTest.class, - com.dotcms.rest.api.CorsFilterTest.class, - com.dotcms.business.LocalTransactionAndCloseDBIfOpenedFactoryTest.class, - com.dotmarketing.startup.runonce.Task05195CreatesDestroyActionAndAssignDestroyDefaultActionsToTheSystemWorkflowTest.class, - com.dotcms.publishing.BundlerUtilTest.class, - com.dotcms.security.multipart.BoundedBufferedReaderTest.class + com.dotcms.rest.api.v1.configuration.ConfigurationResourceTest.class, + com.dotcms.rest.api.v1.page.NavResourceTest.class, + com.dotcms.rest.api.v1.page.PageResourceTest.class, + com.dotcms.rest.api.v1.page.PageRenderSourcesResourceTest.class, + com.dotcms.rest.api.v1.temp.TempFileResourceTest.class, + com.dotcms.rest.api.v1.content.ContentVersionResourceIntegrationTest.class, + com.dotcms.rest.api.v1.content.ContentResourceIntegrationTest.class, + com.dotcms.rest.api.v1.container.ContainerResourceIntegrationTest.class, + com.dotcms.rest.api.v1.container.ContainerResourceHostResolutionIT.class, + com.dotcms.rest.api.v1.theme.ThemeResourceIntegrationTest.class, + com.dotcms.rest.api.v1.vtl.VTLResourceIntegrationTest.class, + com.dotcms.rest.api.v1.contenttype.ContentTypeResourceIssue15124Test.class, + com.dotcms.rest.api.v1.contenttype.FieldResourceTest.class, + com.dotcms.rest.api.v1.contenttype.ContentTypeResourceTest.class, + Chainable404StorageCacheTest.class, + FileStorageAPITest.class, + com.dotcms.analytics.metrics.QueryParameterValuesTransformerTest.class, + QuartzUtilsTest.class, + DotConnectTest.class, + com.dotcms.contenttype.model.field.layout.FieldUtilTest.class, + com.dotmarketing.portlets.contentlet.business.HostAPITest.class, + com.dotcms.content.elasticsearch.business.IndiciesFactoryTest.class, + com.dotcms.content.elasticsearch.business.ESIndexSpeedTest.class, + com.dotcms.content.elasticsearch.business.ES6UpgradeTest.class, + com.dotcms.content.elasticsearch.business.ESContentFactoryImplTest.class, + com.dotcms.graphql.datafetcher.page.ContentMapDataFetcherTest.class, + com.dotcms.graphql.datafetcher.RelationshipFieldDataFetcherTest.class, + com.dotcms.rest.StoryBlockMarkdownPopulatorTest.class }) + public class MainSuite1b { } diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite2a.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite2a.java index d47e48f30a57..e38d6e6c3b25 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite2a.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite2a.java @@ -1,113 +1,122 @@ package com.dotcms; +import com.dotcms.ai.workflow.OpenAIAutoTagActionletTest; +import com.dotcms.business.interceptor.InterceptorHandlerTest; +import com.dotcms.content.elasticsearch.util.ESMappingUtilHelperTest; +import com.dotcms.contenttype.business.DotAssetBaseTypeToContentTypeStrategyImplTest; +import com.dotcms.contenttype.test.DotAssetAPITest; +import com.dotcms.dotpubsub.PostgresPubSubImplTest; +import com.dotcms.ema.EMAWebInterceptorTest; +import com.dotcms.enterprise.cluster.ClusterFactoryTest; import com.dotcms.junit.MainBaseSuite; +import com.dotcms.mock.request.CachedParameterDecoratorTest; +import com.dotcms.publisher.bundle.business.BundleFactoryTest; +import com.dotcms.publisher.business.PublishAuditAPITest; +import com.dotcms.publisher.util.PushedAssetUtilTest; +import com.dotcms.publishing.PublisherFilterImplTest; +import com.dotcms.publishing.PushPublishFiltersInitializerTest; +import com.dotcms.rendering.velocity.directive.DotParseTest; +import com.dotcms.rendering.velocity.servlet.VelocityServletIntegrationTest; +import com.dotcms.rest.BundleResourceTest; +import com.dotcms.rest.api.v1.apps.AppsResourceTest; +import com.dotcms.rest.api.v1.folder.FolderResourceTest; +import com.dotcms.rest.api.v1.maintenance.MaintenanceResourceIntegrationTest; +import com.dotcms.rest.api.v1.pushpublish.PushPublishFilterResourceTest; +import com.dotcms.rest.api.v1.user.UserResourceIntegrationTest; +import com.dotcms.saml.IdentityProviderConfigurationFactoryTest; +import com.dotcms.saml.SamlConfigurationServiceTest; +import com.dotcms.security.apps.AppsAPIImplTest; +import com.dotcms.security.apps.AppsCacheImplTest; +import com.dotcms.translate.GoogleTranslationServiceIntegrationTest; +import com.dotmarketing.image.focalpoint.FocalPointAPITest; +import com.dotmarketing.portlets.cmsmaintenance.factories.CMSMaintenanceFactoryTest; +import com.dotmarketing.portlets.containers.business.ContainerFactoryImplTest; +import com.dotmarketing.portlets.containers.business.ContainerStructureFinderStrategyResolverTest; +import com.dotmarketing.portlets.contentlet.model.IntegrationResourceLinkTest; +import com.dotmarketing.portlets.fileassets.business.FileAssetAPIImplIntegrationTest; +import com.dotmarketing.portlets.fileassets.business.FileAssetFactoryIntegrationTest; +import com.dotmarketing.portlets.folders.model.FolderTest; +import com.dotmarketing.portlets.templates.business.TemplateFactoryImplTest; +import com.dotmarketing.portlets.workflows.actionlet.PushNowActionletTest; +import com.dotmarketing.portlets.workflows.model.TestWorkflowAction; +import com.dotmarketing.quartz.job.CleanUpFieldReferencesJobTest; +import com.dotmarketing.startup.runonce.Task05225RemoveLoadRecordsToIndexTest; +import com.dotmarketing.startup.runonce.Task05305AddPushPublishFilterColumnTest; +import com.dotmarketing.startup.runonce.Task05350AddDotSaltClusterColumnTest; +import com.dotmarketing.startup.runonce.Task240131UpdateLanguageVariableContentTypeTest; +import com.dotmarketing.util.HashBuilderTest; +import com.dotmarketing.util.TestConfig; +import com.liferay.portal.language.LanguageUtilTest; +import org.apache.felix.framework.OSGIUtilTest; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; +/* grep -l -r "@Test" dotCMS/src/integration-test */ +/* ./gradlew integrationTest -Dtest.single=com.dotcms.MainSuite */ + /** - * Integration test suite shard 3 of 7. - * - * Shards are balanced on measured per-class test time so the slowest shard - * bounds the CI critical path as tightly as possible. When adding a test, - * put it in the shard with the lowest total time rather than appending here - * by habit - see .github/test-matrix.yml for the shard list. - * - * Classes are fully qualified so that rebalancing does not churn imports. + * NOTE: LET'S AVOID ADDING MORE TESTS TO THIS SUITE, THIS ONE IS TAKING ALMOST TWICE THE TIME TO RUN THAN THE OTHERS */ @RunWith(MainBaseSuite.class) @SuiteClasses({ // Data-scanning tests run FIRST on purpose. // Integration tests accumulate content and never clean up, so anything - // that walks the whole dataset (executeUpgrade, findAll*) costs - // O(all content created so far). Scheduled late these pay for every - // preceding test's leftovers. Keep new full-scan tests in this block. - com.dotcms.content.elasticsearch.business.ContentletIndexAPIImplMappingTimeoutIT.class, + // that walks the whole dataset (findAllContent) costs O(all content + // created so far). Scheduled late these pay for every preceding test's + // leftovers. Keep new full-scan tests in this block. + com.dotmarketing.factories.MultiTreeAPITest.class, - com.dotcms.browser.BrowserAPITest.class, - com.dotcms.contenttype.business.ContentTypeDestroyAPIImplTest.class, - com.dotmarketing.portlets.contentlet.business.HostAPITest.class, - com.dotcms.contenttype.test.ContentResourceTest.class, - com.dotmarketing.portlets.contentlet.model.ContentletIntegrationTest.class, - com.dotcms.languagevariable.business.LanguageVariableAPITest.class, - com.dotmarketing.quartz.job.DropOldContentVersionsJobTest.class, - com.dotcms.experiments.business.ExperimentUrlPatternCalculatorIntegrationTest.class, - com.dotcms.rest.api.v1.versionable.VersionableResourceTest.class, - com.dotcms.enterprise.publishing.remote.PushPublishBundleGeneratorTest.class, - com.dotmarketing.portlets.fileassets.business.FileAssetAPITest.class, - com.dotcms.content.elasticsearch.business.ESSiteSearchAPITest.class, - com.dotcms.contenttype.test.ContentTypeResourceTest.class, - com.dotcms.rest.api.v1.authentication.ResetPasswordTokenUtilTest.class, - com.dotcms.contenttype.test.FieldFactoryImplTest.class, - com.dotcms.rest.api.v1.folder.FolderResourceSearchTest.class, - com.dotcms.keyvalue.business.KeyValueAPITest.class, - com.dotcms.publisher.business.PublisherAPIImplTest.class, - com.dotcms.experiments.business.IndexRegexUrlPatterStrategyIntegrationTest.class, - com.dotmarketing.portlets.personas.business.PersonaAPITest.class, - com.dotcms.graphql.datafetcher.RelationshipFieldDataFetcherTest.class, - com.dotcms.ai.viewtool.SearchToolTest.class, - com.dotcms.graphql.datafetcher.page.VanityURLFetcherTest.class, - com.dotmarketing.startup.runalways.Task00050LoadAppsSecretsTest.class, - com.dotcms.contenttype.test.ContentTypeImportExportTest.class, - com.dotcms.integritycheckers.ContentPageIntegrityCheckerTest.class, - com.dotmarketing.portlets.fileassets.business.FileAssetAPIImplIntegrationTest.class, - com.dotcms.csspreproc.CSSCacheTest.class, - com.dotcms.analytics.track.collectors.PageDetailCollectorTest.class, - com.dotcms.telemetry.collectors.experiment.CountVariantsInAllRunningExperimentsMetricTypeTest.class, - com.dotcms.integritycheckers.ContentFileAssetIntegrityCheckerTest.class, - com.dotcms.rest.api.v1.drive.ContentDriveHelperContentletAPIComparisonTest.class, - com.dotmarketing.portlets.structure.transform.ContentletRelationshipsTransformerTest.class, - com.dotcms.notification.business.NotificationAPITest.class, - com.dotcms.jitsu.validators.AnalyticsValidatorUtilTest.class, - com.dotcms.rest.api.v1.temp.TempFileResourceTest.class, - com.dotmarketing.business.portal.PortletAPIImplTest.class, com.dotcms.rest.api.v1.workflow.WorkflowResourceResponseCodeIntegrationTest.class, - com.dotcms.rendering.velocity.VelocityUtilTest.class, - com.dotmarketing.image.focalpoint.FocalPointAPITest.class, - com.dotmarketing.business.IdentifierConsistencyIntegrationTest.class, - com.dotcms.rest.api.v1.menu.MenuResourceTest.class, - com.dotcms.publishing.manifest.CSVManifestReaderTest.class, - com.dotmarketing.common.db.ParamsSetterTest.class, - com.dotcms.enterprise.publishing.remote.bundler.ContentBundlerTest.class, - org.apache.velocity.runtime.parser.node.SimpleNodeTest.class, - com.dotmarketing.portlets.contentlet.action.ImportContentletsActionSmokeTest.class, - com.dotcms.cache.lettuce.RedisClientTest.class, - com.dotmarketing.filters.AutoLoginFilterTest.class, - com.dotmarketing.startup.runonce.Task210218MigrateUserProxyTableTest.class, - com.dotcms.ai.util.ContentToStringUtilTest.class, - com.dotcms.workflow.helper.TestSystemActionMappingsHandlerMerger.class, - com.dotmarketing.startup.runonce.Task220512UpdateNoHTMLRegexValueTest.class, - com.dotcms.rendering.velocity.ASTMethodTest.class, - org.apache.velocity.tools.view.tools.CookieToolTest.class, - com.dotmarketing.servlets.InitRunnerTest.class, - com.dotcms.contenttype.model.field.layout.FieldLayoutRowSerializerTest.class, + com.dotcms.rest.api.v1.workflow.WorkflowResourceIntegrationTest.class, + com.dotcms.rest.api.v1.workflow.WorkflowResourceLicenseIntegrationTest.class, + com.dotcms.rest.api.v1.authentication.ResetPasswordResourceIntegrationTest.class, + com.dotcms.rest.api.v1.authentication.CreateJsonWebTokenResourceIntegrationTest.class, + com.dotcms.rest.api.v1.relationships.RelationshipsResourceTest.class, + com.dotcms.rest.api.v1.contenttype.ContentTypeResourceUpdateMetadataTest.class, + com.dotcms.rest.api.v2.contenttype.FieldResourceTest.class, + com.dotcms.rest.api.v3.contenttype.FieldResourceTest.class, + com.dotcms.rest.api.v3.contenttype.MoveFieldFormTest.class, + com.dotcms.rest.api.CorsFilterTest.class, + com.dotcms.rest.elasticsearch.ESContentResourcePortletTest.class, + com.dotcms.filters.VanityUrlFilterTest.class, + com.dotcms.vanityurl.business.VanityUrlAPITest.class, + com.dotmarketing.portlets.fileassets.business.FileAssetAPITest.class, + com.dotmarketing.portlets.languagesmanager.business.LanguageAPITest.class, com.dotmarketing.portlets.languagesmanager.business.LanguageFactoryIntegrationTest.class, - com.dotmarketing.startup.runonce.Task211012AddCompanyDefaultLanguageTest.class, - com.dotmarketing.startup.runonce.Task251103AddStylePropertiesColumnInMultiTreeTest.class, - com.dotmarketing.startup.runonce.Task210319CreateStorageTableTest.class, - com.dotmarketing.startup.StartupTasksExecutorDataTest.class, - com.dotcms.business.bytebuddy.ByteBuddyAdviceWeavingTest.class, - com.dotcms.content.elasticsearch.business.IndiciesFactoryTest.class, - com.dotmarketing.db.DbConnectionFactoryTest.class, - com.dotcms.visitor.filter.logger.VisitorLoggerTest.class, - com.dotmarketing.startup.StartupTasksExecutorTest.class, - com.dotmarketing.startup.runonce.Task240513UpdateContentTypesSystemFieldTest.class, - com.dotmarketing.startup.runonce.Task210316UpdateLayoutIconsTest.class, - com.dotmarketing.startup.runonce.Task220829CreateExperimentsTableTest.class, - com.dotcms.cdi.SimpleDataProviderWeldRunnerInjectionIT.class, - com.dotcms.analytics.attributes.CustomAttributeFactoryTest.class, - com.dotmarketing.cache.FolderCacheImplIntegrationTest.class, - com.dotcms.cache.KeyValueCacheImplTest.class, - com.dotcms.publishing.manifest.ManifestUtilTest.class, - com.dotmarketing.business.IdentifierAPITest.class, - com.dotcms.rendering.velocity.viewtools.MessageToolTest.class, - com.dotcms.rendering.js.JsEngineTest.class, - com.dotmarketing.portlets.rules.conditionlet.ConditionletOSGIFTest.class, - com.dotmarketing.startup.runonce.Task210321RemoveOldMetadataFilesTest.class, - com.dotcms.enterprise.publishing.staticpublishing.AWSS3PublisherTest.class, - com.dotmarketing.portlets.contentlet.model.ContentletDependenciesTest.class, - com.dotmarketing.startup.runonce.Task05160MultiTreeAddPersonalizationColumnAndChangingPKTest.class, - com.dotmarketing.startup.runalways.Task00001LoadSchemaIntegrationTest.class + com.dotmarketing.portlets.linkchecker.business.LinkCheckerAPITest.class, + com.dotmarketing.portlets.contentlet.util.ContentletUtilTest.class, + com.dotmarketing.portlets.contentlet.business.ContentletCheckInTest.class, + com.dotmarketing.portlets.contentlet.business.ContentletFactoryTest.class, + ContainerStructureFinderStrategyResolverTest.class, + com.dotmarketing.portlets.contentlet.business.ContentletAPITest.class, + com.dotmarketing.portlets.contentlet.model.ContentletIntegrationTest.class, + com.dotmarketing.portlets.contentlet.transform.BinaryToMapTransformerTest.class, + com.dotmarketing.portlets.contentlet.transform.ContentletTransformerTest.class, + com.dotmarketing.portlets.contentlet.transform.WidgetViewStrategyTest.class, + com.dotmarketing.portlets.contentlet.ajax.ContentletAjaxTest.class, + com.dotmarketing.portlets.workflows.business.SaveContentDraftActionletTest.class, + com.dotmarketing.portlets.workflows.business.WorkflowFactoryTest.class, + com.dotmarketing.portlets.workflows.business.SaveContentActionletTest.class, + com.dotmarketing.portlets.workflows.business.WorkflowAPIMultiLanguageTest.class, + com.dotmarketing.portlets.workflows.business.WorkflowAPITest.class, + com.dotmarketing.portlets.workflows.model.WorkflowSearcherTest.class, + com.dotmarketing.portlets.workflows.model.SystemActionWorkflowActionMappingTest.class, + com.dotmarketing.portlets.workflows.actionlet.FourEyeApproverActionletTest.class, + com.dotmarketing.portlets.workflows.actionlet.SaveContentActionletTest.class, + com.dotmarketing.portlets.workflows.actionlet.SaveContentActionletWithTagsTest.class, + com.dotmarketing.portlets.workflows.actionlet.CopyActionletTest.class, + com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionletTest.class, + com.dotmarketing.portlets.personas.business.PersonaAPITest.class, + com.dotmarketing.portlets.personas.business.DeleteMultiTreeUsedPersonaTagJobTest.class, + com.dotmarketing.portlets.links.business.MenuLinkAPITest.class, + com.dotmarketing.portlets.links.factories.LinkFactoryTest.class, + com.dotmarketing.portlets.categories.business.CategoryAPITest.class, + com.dotmarketing.filters.FiltersTest.class, + InterceptorHandlerTest.class, + com.dotcms.graphql.datafetcher.page.NumberContentsDataFetcherTest.class, + com.dotcms.rest.AuditPublishingResourceTest.class, + MaintenanceResourceIntegrationTest.class }) public class MainSuite2a { diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java index c8edc4e135e8..fd104dbb79dd 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java @@ -1,108 +1,569 @@ package com.dotcms; +import com.dotcms.ai.app.ConfigServiceTest; +import com.dotcms.ai.client.AIProxyClientTest; +import com.dotcms.ai.listener.EmbeddingContentListenerTest; +import com.dotcms.ai.viewtool.AIViewToolTest; +import com.dotcms.ai.viewtool.CompletionsToolTest; +import com.dotcms.ai.viewtool.EmbeddingsToolTest; +import com.dotcms.ai.viewtool.SearchToolTest; +import com.dotcms.ai.workflow.OpenAIAutoTagActionletTest; +import com.dotcms.ai.workflow.OpenAIContentPromptActionletTest; +import com.dotcms.analytics.attributes.CustomAttributeAPIImplTest; +import com.dotcms.analytics.attributes.CustomAttributeFactoryTest; +import com.dotcms.analytics.bayesian.BayesianAPIImplIT; +import com.dotcms.analytics.track.collectors.AsyncVanitiesCollectorTest; +import com.dotcms.analytics.track.collectors.BasicProfileCollectorTest; +import com.dotcms.analytics.track.collectors.FilesCollectorTest; +import com.dotcms.analytics.track.collectors.PageDetailCollectorTest; +import com.dotcms.analytics.track.collectors.PagesCollectorTest; +import com.dotcms.analytics.track.collectors.SyncVanitiesCollectorTest; +import com.dotcms.analytics.track.collectors.WebEventsCollectorServiceImplTest; +import com.dotcms.api.web.HttpServletRequestImpersonatorTest; +import com.dotcms.auth.providers.saml.v1.DotSamlResourceTest; +import com.dotcms.auth.providers.saml.v1.SAMLHelperTest; +import com.dotcms.business.SystemAPITest; +import com.dotcms.business.SystemTableFactoryTest; +import com.dotcms.cache.lettuce.DotObjectCodecTest; +import com.dotcms.cache.lettuce.LettuceCacheTest; +import com.dotcms.cache.lettuce.RedisClientTest; +import com.dotcms.cdi.SimpleDataProviderWeldRunnerInjectionIT; +import com.dotcms.cdi.SimpleInjectionIT; +import com.dotcms.cdi.SimpleJUnit4InjectionIT; +import com.dotcms.content.business.ObjectMapperTest; +import com.dotcms.content.business.json.ContentletJsonAPITest; +import com.dotcms.content.business.json.LegacyJSONObjectRenderTest; +import com.dotcms.content.elasticsearch.business.ESIndexAPITest; +import com.dotcms.content.elasticsearch.util.ESMappingUtilHelperTest; +import com.dotcms.content.model.hydration.MetadataDelegateTest; +import com.dotcms.contenttype.business.ContentTypeInitializerTest; +import com.dotcms.contenttype.business.DotAssetBaseTypeToContentTypeStrategyImplTest; +import com.dotcms.contenttype.business.FileAssetBaseTypeToContentTypeStrategyImplTest; +import com.dotcms.contenttype.business.StoryBlockAPITest; +import com.dotcms.contenttype.business.uniquefields.extratable.DBUniqueFieldValidationStrategyTest; +import com.dotcms.contenttype.business.uniquefields.extratable.UniqueFieldDataBaseUtilTest; +import com.dotcms.contenttype.test.DotAssetAPITest; +import com.dotcms.csspreproc.CSSCacheTest; +import com.dotcms.csspreproc.CSSPreProcessServletIT; +import com.dotcms.dotpubsub.PostgresPubSubImplTest; +import com.dotcms.dotpubsub.RedisPubSubImplTest; +import com.dotcms.ema.EMAWebInterceptorTest; +import com.dotcms.enterprise.cluster.ClusterFactoryTest; +import com.dotcms.enterprise.publishing.bundler.URLMapBundlerTest; +import com.dotcms.enterprise.publishing.remote.StaticPushPublishBundleGeneratorTest; +import com.dotcms.enterprise.publishing.remote.bundler.ContainerBundlerTest; +import com.dotcms.enterprise.publishing.remote.bundler.ContentBundlerTest; +import com.dotcms.enterprise.publishing.remote.bundler.ContentTypeBundlerTest; +import com.dotcms.enterprise.publishing.remote.bundler.FolderBundlerTest; +import com.dotcms.enterprise.publishing.remote.bundler.HostBundlerTest; +import com.dotcms.enterprise.publishing.remote.bundler.LinkBundlerTest; +import com.dotcms.enterprise.publishing.remote.bundler.TemplateBundlerTest; +import com.dotcms.enterprise.publishing.remote.bundler.WorkflowBundlerTest; +import com.dotcms.enterprise.publishing.remote.handler.ContentHandlerTest; +import com.dotcms.enterprise.publishing.remote.handler.ContentWorkflowHandlerTest; +import com.dotcms.enterprise.publishing.remote.handler.HandlerUtilTest; +import com.dotcms.enterprise.publishing.staticpublishing.AWSS3PublisherTest; +import com.dotcms.enterprise.publishing.staticpublishing.LanguageFolderTest; +import com.dotcms.experiments.business.IndexRegexUrlPatterStrategyIntegrationTest; +import com.dotcms.experiments.business.RootIndexRegexUrlPatterStrategyIntegrationTest; +import com.dotcms.filters.interceptor.meta.MetaWebInterceptorTest; +import com.dotcms.integritycheckers.ContentFileAssetIntegrityCheckerTest; +import com.dotcms.integritycheckers.ContentPageIntegrityCheckerTest; +import com.dotcms.integritycheckers.HostIntegrityCheckerTest; +import com.dotcms.integritycheckers.IntegrityUtilTest; +import com.dotcms.jobs.business.api.JobQueueManagerAPITest; import com.dotcms.junit.MainBaseSuite; +import com.dotcms.mail.MailAPIImplTest; +import com.dotcms.mock.request.CachedParameterDecoratorTest; +import com.dotcms.publisher.bundle.business.BundleAPITest; +import com.dotcms.publisher.bundle.business.BundleFactoryTest; +import com.dotcms.publisher.business.PublishAuditAPITest; +import com.dotcms.publisher.receiver.BundlePublisherTest; +import com.dotcms.publisher.util.DependencyManagerTest; +import com.dotcms.publisher.util.PushedAssetUtilTest; +import com.dotcms.publishing.BundlerUtilTest; +import com.dotcms.publishing.PublisherFilterImplTest; +import com.dotcms.publishing.PushPublishFiltersInitializerTest; +import com.dotcms.publishing.manifest.CSVManifestBuilderTest; +import com.dotcms.publishing.manifest.CSVManifestReaderTest; +import com.dotcms.publishing.manifest.ManifestReaderFactoryTest; +import com.dotcms.publishing.manifest.ManifestUtilTest; +import com.dotcms.rendering.velocity.directive.DotParseTest; +import com.dotcms.rendering.velocity.servlet.VelocityServletIntegrationTest; +import com.dotcms.rendering.velocity.viewtools.DotTemplateToolTest; +import com.dotcms.rendering.velocity.viewtools.FileToolTest; +import com.dotcms.rendering.velocity.viewtools.JSONToolTest; +import com.dotcms.rendering.velocity.viewtools.MessageToolTest; +import com.dotcms.rendering.velocity.viewtools.XmlToolTest; +import com.dotcms.rendering.velocity.viewtools.content.StoryBlockMapTest; +import com.dotcms.rendering.velocity.viewtools.content.StoryBlockTest; +import com.dotcms.rest.BundleResourceTest; +import com.dotcms.rest.api.v1.announcements.AnnouncementsHelperIntegrationTest; +import com.dotcms.rest.api.v1.announcements.RemoteAnnouncementsLoaderIntegrationTest; +import com.dotcms.rest.api.v1.apps.SiteViewPaginatorIntegrationTest; +import com.dotcms.rest.api.v1.apps.view.AppsInterpolationTest; +import com.dotcms.rest.api.v1.asset.AssetPathResolverImplIntegrationTest; +import com.dotcms.rest.api.v1.asset.WebAssetHelperIntegrationTest; +import com.dotcms.rest.api.v1.authentication.ResetPasswordTokenUtilTest; +import com.dotcms.rest.api.v1.folder.FolderResourceSearchTest; +import com.dotcms.rest.api.v1.folder.FolderResourceTest; +import com.dotcms.rest.api.v1.maintenance.ClusterLogCollectorTest; +import com.dotcms.rest.api.v1.menu.MenuResourceTest; +import com.dotcms.rest.api.v1.publishing.BundleManagementResourceIntegrationTest; +import com.dotcms.rest.api.v1.publishing.PublishingResourceIntegrationTest; +import com.dotcms.rest.api.v1.pushpublish.PushPublishFilterResourceTest; +import com.dotcms.rest.api.v1.system.ConfigurationHelperTest; +import com.dotcms.rest.api.v1.system.permission.PermissionResourceIntegrationTest; +import com.dotcms.rest.api.v1.taillog.TailLogResourceTest; +import com.dotcms.rest.api.v1.user.UserResourceIntegrationTest; +import com.dotcms.rest.api.v2.asset.WebAssetResourceV2IntegrationTest; +import com.dotcms.saml.IdentityProviderConfigurationFactoryTest; +import com.dotcms.saml.SamlConfigurationServiceTest; +import com.dotcms.security.apps.AppsCacheImplTest; +import com.dotcms.security.multipart.BoundedBufferedReaderTest; +import com.dotcms.security.multipart.ContentDispositionFileNameParserTest; +import com.dotcms.security.multipart.SecureFileValidatorTest; +import com.dotcms.storage.FileMetadataAPITest; +import com.dotcms.storage.StoragePersistenceAPITest; +import com.dotcms.storage.repository.HashedLocalFileRepositoryManagerTest; +import com.dotcms.timemachine.business.TimeMachineAPITest; +import com.dotcms.translate.GoogleTranslationServiceIntegrationTest; +import com.dotcms.util.content.json.PopulateContentletAsJSONUtilTest; +import com.dotcms.variant.VariantAPITest; +import com.dotcms.variant.VariantFactoryTest; +import com.dotcms.variant.business.VariantCacheTest; +import com.dotmarketing.beans.HostTest; +import com.dotmarketing.business.IdentifierCacheImplTest; +import com.dotmarketing.business.PermissionBitFactoryImplTest; +import com.dotmarketing.business.VersionableFactoryImplTest; +import com.dotmarketing.business.helper.PermissionHelperTest; +import com.dotmarketing.common.db.DBTimeZoneCheckTest; +import com.dotmarketing.filters.AutoLoginFilterTest; +import com.dotmarketing.filters.CMSUrlUtilIntegrationTest; +import com.dotmarketing.image.focalpoint.FocalPointAPITest; +import com.dotmarketing.osgi.GenericBundleActivatorIntegrationTest; +import com.dotmarketing.portlets.browser.BrowserUtilTest; +import com.dotmarketing.portlets.browser.ajax.BrowserAjaxTest; +import com.dotmarketing.portlets.categories.business.CategoryFactoryTest; +import com.dotmarketing.portlets.cmsmaintenance.factories.CMSMaintenanceFactoryTest; +import com.dotmarketing.portlets.containers.business.ContainerFactoryImplTest; +import com.dotmarketing.portlets.contentlet.business.ContentletCacheImplTest; +import com.dotmarketing.portlets.contentlet.model.ContentletDependenciesTest; +import com.dotmarketing.portlets.contentlet.model.IntegrationResourceLinkTest; +import com.dotmarketing.portlets.fileassets.business.FileAssetAPIImplIntegrationTest; +import com.dotmarketing.portlets.fileassets.business.FileAssetFactoryIntegrationTest; +import com.dotmarketing.portlets.folders.business.FolderFactoryImplTest; +import com.dotmarketing.portlets.folders.model.FolderTest; +import com.dotmarketing.portlets.templates.business.FileAssetTemplateUtilTest; +import com.dotmarketing.portlets.templates.business.TemplateFactoryImplTest; +import com.dotmarketing.portlets.workflows.actionlet.MoveContentActionletTest; +import com.dotmarketing.portlets.workflows.actionlet.PushNowActionletTest; +import com.dotmarketing.portlets.workflows.actionlet.SaveContentAsDraftActionletIntegrationTest; +import com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionletAbortTest; +import com.dotmarketing.portlets.workflows.model.TestWorkflowAction; +import com.dotmarketing.quartz.DotStatefulJobTest; +import com.dotmarketing.quartz.job.CleanUpFieldReferencesJobTest; +import com.dotmarketing.quartz.job.DropOldContentVersionsJobTest; +import com.dotmarketing.quartz.job.EncryptPlainPasswordsJobTest; +import com.dotmarketing.quartz.job.IntegrityDataGenerationJobTest; +import com.dotmarketing.quartz.job.PopulateContentletAsJSONJobTest; +import com.dotmarketing.quartz.job.PruneTimeMachineBackupJobTest; +import com.dotmarketing.startup.StartupTasksExecutorDataTest; +import com.dotmarketing.startup.StartupTasksExecutorTest; +import com.dotmarketing.startup.runalways.Task00050LoadAppsSecretsTest; +import com.dotmarketing.startup.runonce.Task05195CreatesDestroyActionAndAssignDestroyDefaultActionsToTheSystemWorkflowTest; +import com.dotmarketing.startup.runonce.Task05210CreateDefaultDotAssetTest; +import com.dotmarketing.startup.runonce.Task05225RemoveLoadRecordsToIndexTest; +import com.dotmarketing.startup.runonce.Task05305AddPushPublishFilterColumnTest; +import com.dotmarketing.startup.runonce.Task05350AddDotSaltClusterColumnTest; +import com.dotmarketing.startup.runonce.Task05370AddAppsPortletToLayoutTest; +import com.dotmarketing.startup.runonce.Task05380ChangeContainerPathToAbsoluteTest; +import com.dotmarketing.startup.runonce.Task05390MakeRoomForLongerJobDetailTest; +import com.dotmarketing.startup.runonce.Task05395RemoveEndpointIdForeignKeyInIntegrityResolverTablesIntegrationTest; +import com.dotmarketing.startup.runonce.Task201013AddNewColumnsToIdentifierTableTest; +import com.dotmarketing.startup.runonce.Task201014UpdateColumnsValuesInIdentifierTableTest; +import com.dotmarketing.startup.runonce.Task201102UpdateColumnSitelicTableTest; +import com.dotmarketing.startup.runonce.Task210218MigrateUserProxyTableTest; +import com.dotmarketing.startup.runonce.Task210319CreateStorageTableTest; +import com.dotmarketing.startup.runonce.Task210321RemoveOldMetadataFilesTest; +import com.dotmarketing.startup.runonce.Task210506UpdateStorageTableTest; +import com.dotmarketing.startup.runonce.Task210510UpdateStorageTableDropMetadataColumnTest; +import com.dotmarketing.startup.runonce.Task210520UpdateAnonymousEmailTest; +import com.dotmarketing.startup.runonce.Task210527DropReviewFieldsFromContentletTableTest; +import com.dotmarketing.startup.runonce.Task210719CleanUpTitleFieldTest; +import com.dotmarketing.startup.runonce.Task210802UpdateStructureTableTest; +import com.dotmarketing.startup.runonce.Task210805DropUserProxyTableTest; +import com.dotmarketing.startup.runonce.Task210816DeInodeRelationshipTest; +import com.dotmarketing.startup.runonce.Task210901UpdateDateTimezonesTest; +import com.dotmarketing.startup.runonce.Task211007RemoveNotNullConstraintFromCompanyMXColumnTest; +import com.dotmarketing.startup.runonce.Task211012AddCompanyDefaultLanguageTest; +import com.dotmarketing.startup.runonce.Task211101AddContentletAsJsonColumnTest; +import com.dotmarketing.startup.runonce.Task211103RenameHostNameLabelTest; +import com.dotmarketing.startup.runonce.Task220202RemoveFKStructureFolderConstraintTest; +import com.dotmarketing.startup.runonce.Task220203RemoveFolderInodeConstraintTest; +import com.dotmarketing.startup.runonce.Task220214AddOwnerAndIDateToFolderTableTest; +import com.dotmarketing.startup.runonce.Task260720AddDefaultBaseTypeToFolderTableTest; +import com.dotmarketing.startup.runonce.Task220215MigrateDataFromInodeToFolderTest; +import com.dotmarketing.startup.runonce.Task220330ChangeVanityURLSiteFieldTypeTest; +import com.dotmarketing.startup.runonce.Task220401CreateClusterLockTableTest; +import com.dotmarketing.startup.runonce.Task220402UpdateDateTimezonesTest; +import com.dotmarketing.startup.runonce.Task220413IncreasePublishedPushedAssetIdColTest; +import com.dotmarketing.startup.runonce.Task220512UpdateNoHTMLRegexValueTest; +import com.dotmarketing.startup.runonce.Task220606UpdatePushNowActionletNameTest; +import com.dotmarketing.startup.runonce.Task220822CreateVariantTableTest; +import com.dotmarketing.startup.runonce.Task220824CreateDefaultVariantTest; +import com.dotmarketing.startup.runonce.Task220825CreateVariantFieldTest; +import com.dotmarketing.startup.runonce.Task220829CreateExperimentsTableTest; +import com.dotmarketing.startup.runonce.Task220912UpdateCorrectShowOnMenuPropertyTest; +import com.dotmarketing.startup.runonce.Task220928AddLookbackWindowColumnToExperimentTest; +import com.dotmarketing.startup.runonce.Task221007AddVariantIntoPrimaryKeyTest; +import com.dotmarketing.startup.runonce.Task230110MakeSomeSystemFieldsRemovableByBaseTypeTest; +import com.dotmarketing.startup.runonce.Task230328AddMarkedForDeletionColumnTest; +import com.dotmarketing.startup.runonce.Task230426AlterVarcharLengthOfLockedByColTest; +import com.dotmarketing.startup.runonce.Task230523CreateVariantFieldInContentletIntegrationTest; +import com.dotmarketing.startup.runonce.Task230701AddHashIndicesToWorkflowTablesTest; +import com.dotmarketing.startup.runonce.Task230707CreateSystemTableTest; +import com.dotmarketing.startup.runonce.Task230713IncreaseDisabledWysiwygColumnSizeTest; +import com.dotmarketing.startup.runonce.Task231109AddPublishDateToContentletVersionInfoTest; +import com.dotmarketing.startup.runonce.Task240102AlterVarcharLengthOfRelationTypeTest; +import com.dotmarketing.startup.runonce.Task240111AddInodeAndIdentifierLeftIndexesTest; +import com.dotmarketing.startup.runonce.Task240112AddMetadataColumnToStructureTableTest; +import com.dotmarketing.startup.runonce.Task240131UpdateLanguageVariableContentTypeTest; +import com.dotmarketing.startup.runonce.Task240513UpdateContentTypesSystemFieldTest; +import com.dotmarketing.startup.runonce.Task240530AddDotAIPortletToLayoutTest; +import com.dotmarketing.startup.runonce.Task240606AddVariableColumnToWorkflowTest; +import com.dotmarketing.startup.runonce.Task241013RemoveFullPathLcColumnFromIdentifierTest; +import com.dotmarketing.startup.runonce.Task241015ReplaceLanguagesWithLocalesPortletTest; +import com.dotmarketing.startup.runonce.Task241016AddCustomLanguageVariablesPortletToLayoutTest; +import com.dotmarketing.startup.runonce.Task250107RemoveEsReadOnlyMonitorJobTest; +import com.dotmarketing.startup.runonce.Task250113CreatePostgresJobQueueTablesTest; +import com.dotmarketing.startup.runonce.Task250828CreateCustomAttributeTableTest; +import com.dotmarketing.util.ConfigUtilsTest; +import com.dotmarketing.util.HashBuilderTest; +import com.dotmarketing.util.ITConfigTest; +import com.dotmarketing.util.MaintenanceUtilTest; +import com.dotmarketing.util.ResourceCollectorUtilTest; +import com.dotmarketing.util.TestConfig; +import com.dotmarketing.util.UtilMethodsITest; +import com.dotmarketing.util.contentlet.pagination.PaginatedContentletsIntegrationTest; +import com.liferay.portal.language.LanguageUtilTest; +import org.apache.felix.framework.OSGIUtilTest; +import org.apache.velocity.tools.view.tools.CookieToolTest; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; -/** - * Integration test suite shard 4 of 7. - * - * Shards are balanced on measured per-class test time so the slowest shard - * bounds the CI critical path as tightly as possible. When adding a test, - * put it in the shard with the lowest total time rather than appending here - * by habit - see .github/test-matrix.yml for the shard list. - * - * Classes are fully qualified so that rebalancing does not churn imports. - */ +/* grep -l -r "@Test" dotCMS/src/integration-test */ +/* ./gradlew integrationTest -Dtest.single=com.dotcms.MainSuite */ + + @RunWith(MainBaseSuite.class) @SuiteClasses({ - // Data-scanning tests run FIRST on purpose. - // Integration tests accumulate content and never clean up, so anything - // that walks the whole dataset (executeUpgrade, findAll*) costs - // O(all content created so far). Scheduled late these pay for every - // preceding test's leftovers. Keep new full-scan tests in this block. - com.dotmarketing.quartz.job.CleanUpFieldReferencesJobTest.class, + // Reindex-heavy tests run FIRST on purpose. + // Integration tests accumulate content and never clean up, so a full + // reindex costs O(all content created so far). Scheduled late in a + // 297-class suite these reindex the entire accumulated dataset instead + // of just their own fixtures. Keep new full-reindex tests in this block. + ESMappingUtilHelperTest.class, com.dotmarketing.common.reindex.ReindexThreadTest.class, + com.dotcms.content.elasticsearch.business.ContentletIndexAPIImplMappingTimeoutIT.class, + com.dotmarketing.common.reindex.ReindexAPITest.class, + CleanUpFieldReferencesJobTest.class, + EMAWebInterceptorTest.class, - com.dotcms.rest.api.v1.page.PageResourceTest.class, - com.dotcms.util.ImportUtilTest.class, - com.dotcms.content.elasticsearch.business.ContentletIndexAPIImplTest.class, - com.dotcms.contenttype.business.StoryBlockAPITest.class, - com.dotcms.rendering.velocity.viewtools.content.ContentToolTest.class, - com.dotcms.rest.MapToContentletPopulatorTest.class, + Task220825CreateVariantFieldTest.class, + Task221007AddVariantIntoPrimaryKeyTest.class, com.dotcms.rest.api.v1.template.TemplateResourceTest.class, - com.dotcms.publisher.business.PublisherTest.class, - com.dotcms.content.elasticsearch.business.ESMappingAPITest.class, - org.apache.felix.framework.OSGIUtilTest.class, - com.dotmarketing.portlets.contentlet.business.web.ContentletWebAPIImplIntegrationTest.class, - com.dotcms.contenttype.business.uniquefields.extratable.DBUniqueFieldValidationStrategyTest.class, - com.dotcms.rest.api.v1.theme.ThemeResourceIntegrationTest.class, + Task05380ChangeContainerPathToAbsoluteTest.class, + DotTemplateToolTest.class, + Task05370AddAppsPortletToLayoutTest.class, + FolderFactoryImplTest.class, + DotSamlResourceTest.class, + DotStatefulJobTest.class, + IntegrityDataGenerationJobTest.class, + BundleAPITest.class, + Task05390MakeRoomForLongerJobDetailTest.class, + Task05395RemoveEndpointIdForeignKeyInIntegrityResolverTablesIntegrationTest.class, + JSONToolTest.class, + Task00050LoadAppsSecretsTest.class, + StoragePersistenceAPITest.class, + FileMetadataAPITest.class, + StartupTasksExecutorTest.class, + Task201013AddNewColumnsToIdentifierTableTest.class, + Task201014UpdateColumnsValuesInIdentifierTableTest.class, + AppsInterpolationTest.class, + Task201102UpdateColumnSitelicTableTest.class, + DependencyManagerTest.class, + com.dotcms.rest.api.v1.versionable.VersionableResourceTest.class, + GenericBundleActivatorIntegrationTest.class, + SAMLHelperTest.class, + PermissionHelperTest.class, + ResetPasswordTokenUtilTest.class, + ContainerBundlerTest.class, + ContentTypeBundlerTest.class, + FolderBundlerTest.class, + HostBundlerTest.class, + LinkBundlerTest.class, + TemplateBundlerTest.class, + WorkflowBundlerTest.class, + AutoLoginFilterTest.class, + Task210218MigrateUserProxyTableTest.class, + com.dotmarketing.startup.runonce.Task210316UpdateLayoutIconsTest.class, + Task210319CreateStorageTableTest.class, + Task210321RemoveOldMetadataFilesTest.class, + DBTimeZoneCheckTest.class, + ContentHandlerTest.class, + ESIndexAPITest.class, + FileAssetTemplateUtilTest.class, + Task210506UpdateStorageTableTest.class, + Task210520UpdateAnonymousEmailTest.class, + Task210510UpdateStorageTableDropMetadataColumnTest.class, + StaticPushPublishBundleGeneratorTest.class, + CookieToolTest.class, + CSVManifestBuilderTest.class, + MoveContentActionletTest.class, + Task210527DropReviewFieldsFromContentletTableTest.class, + ContentletCacheImplTest.class, + HostTest.class, + FileToolTest.class, + Task210719CleanUpTitleFieldTest.class, + Task210802UpdateStructureTableTest.class, + MaintenanceUtilTest.class, + BundlePublisherTest.class, + CategoryFactoryTest.class, + Task210805DropUserProxyTableTest.class, + Task210816DeInodeRelationshipTest.class, + ConfigurationHelperTest.class, + CSVManifestReaderTest.class, + Task210901UpdateDateTimezonesTest.class, + DotObjectCodecTest.class, + RedisClientTest.class, + LettuceCacheTest.class, + RedisPubSubImplTest.class, + ManifestReaderFactoryTest.class, + ResourceCollectorUtilTest.class, + Task211007RemoveNotNullConstraintFromCompanyMXColumnTest.class, + Task211012AddCompanyDefaultLanguageTest.class, + HostIntegrityCheckerTest.class, + MetaWebInterceptorTest.class, + BrowserUtilTest.class, + Task211101AddContentletAsJsonColumnTest.class, + ContentletJsonAPITest.class, + VelocityScriptActionletAbortTest.class, + StoryBlockMapTest.class, + HandlerUtilTest.class, + Task211103RenameHostNameLabelTest.class, + MessageToolTest.class, + XmlToolTest.class, + LanguageFolderTest.class, + MailAPIImplTest.class, + CSSCacheTest.class, com.dotcms.rendering.velocity.viewtools.content.BinaryMapTest.class, - com.dotcms.content.elasticsearch.business.ES6UpgradeTest.class, - com.dotcms.rest.api.v2.tags.TagResourceIntegrationTest.class, - com.dotcms.ai.api.OpenAIVisionAPIImplTest.class, + IntegrityUtilTest.class, + Task220202RemoveFKStructureFolderConstraintTest.class, + ContentBundlerTest.class, + ObjectMapperTest.class, + URLMapBundlerTest.class, + PermissionBitFactoryImplTest.class, + Task220203RemoveFolderInodeConstraintTest.class, + Task220214AddOwnerAndIDateToFolderTableTest.class, + Task260720AddDefaultBaseTypeToFolderTableTest.class, + Task220215MigrateDataFromInodeToFolderTest.class, + Task220330ChangeVanityURLSiteFieldTypeTest.class, + Task220402UpdateDateTimezonesTest.class, + Task220413IncreasePublishedPushedAssetIdColTest.class, + com.dotcms.util.pagination.ContainerPaginatorTest.class, + ContentDispositionFileNameParserTest.class, + SecureFileValidatorTest.class, + BoundedBufferedReaderTest.class, + ContentWorkflowHandlerTest.class, + Task220512UpdateNoHTMLRegexValueTest.class, + MetadataDelegateTest.class, + Task220401CreateClusterLockTableTest.class, + Task220606UpdatePushNowActionletNameTest.class, + BundlerUtilTest.class, + MenuResourceTest.class, + AWSS3PublisherTest.class, + ContentTypeInitializerTest.class, + CSSPreProcessServletIT.class, + VariantFactoryTest.class, + VariantAPITest.class, + PaginatedContentletsIntegrationTest.class, + Task220824CreateDefaultVariantTest.class, + Task220822CreateVariantTableTest.class, + Task220829CreateExperimentsTableTest.class, + StoryBlockTest.class, + IdentifierCacheImplTest.class, + VariantCacheTest.class, + VersionableFactoryImplTest.class, + Task220928AddLookbackWindowColumnToExperimentTest.class, + TailLogResourceTest.class, + ClusterLogCollectorTest.class, + BayesianAPIImplIT.class, + ContentletDependenciesTest.class, + SaveContentAsDraftActionletIntegrationTest.class, + StoryBlockAPITest.class, + UtilMethodsITest.class, + Task220912UpdateCorrectShowOnMenuPropertyTest.class, + HashedLocalFileRepositoryManagerTest.class, + ManifestUtilTest.class, + Task230110MakeSomeSystemFieldsRemovableByBaseTypeTest.class, + BrowserAjaxTest.class, + PopulateContentletAsJSONUtilTest.class, + PopulateContentletAsJSONJobTest.class, + Task230328AddMarkedForDeletionColumnTest.class, + StartupTasksExecutorDataTest.class, + Task230426AlterVarcharLengthOfLockedByColTest.class, + AssetPathResolverImplIntegrationTest.class, + WebAssetHelperIntegrationTest.class, + WebAssetResourceV2IntegrationTest.class, + SystemTableFactoryTest.class, + Task230707CreateSystemTableTest.class, + SystemAPITest.class, + Task230701AddHashIndicesToWorkflowTablesTest.class, + Task230713IncreaseDisabledWysiwygColumnSizeTest.class, + ContentPageIntegrityCheckerTest.class, + IndexRegexUrlPatterStrategyIntegrationTest.class, + RootIndexRegexUrlPatterStrategyIntegrationTest.class, + SiteViewPaginatorIntegrationTest.class, + Task230523CreateVariantFieldInContentletIntegrationTest.class, + DropOldContentVersionsJobTest.class, + Task231109AddPublishDateToContentletVersionInfoTest.class, + Task240102AlterVarcharLengthOfRelationTypeTest.class, + Task240111AddInodeAndIdentifierLeftIndexesTest.class, + AnnouncementsHelperIntegrationTest.class, + RemoteAnnouncementsLoaderIntegrationTest.class, + Task240112AddMetadataColumnToStructureTableTest.class, + AIViewToolTest.class, + SearchToolTest.class, + EmbeddingsToolTest.class, + CompletionsToolTest.class, + ConfigServiceTest.class, + AIProxyClientTest.class, + TimeMachineAPITest.class, + Task240513UpdateContentTypesSystemFieldTest.class, + PruneTimeMachineBackupJobTest.class, + CMSUrlUtilIntegrationTest.class, + ContentFileAssetIntegrityCheckerTest.class, + ITConfigTest.class, + Task240530AddDotAIPortletToLayoutTest.class, + EmbeddingContentListenerTest.class, + Task240606AddVariableColumnToWorkflowTest.class, + OpenAIContentPromptActionletTest.class, + JobQueueManagerAPITest.class, + ConfigUtilsTest.class, + SimpleInjectionIT.class, + SimpleDataProviderWeldRunnerInjectionIT.class, + SimpleJUnit4InjectionIT.class, + LegacyJSONObjectRenderTest.class, + Task241013RemoveFullPathLcColumnFromIdentifierTest.class, + Task250113CreatePostgresJobQueueTablesTest.class, + UniqueFieldDataBaseUtilTest.class, + DBUniqueFieldValidationStrategyTest.class, + Task241015ReplaceLanguagesWithLocalesPortletTest.class, + Task241016AddCustomLanguageVariablesPortletToLayoutTest.class, + WebEventsCollectorServiceImplTest.class, + BasicProfileCollectorTest.class, + PagesCollectorTest.class, + PageDetailCollectorTest.class, + FilesCollectorTest.class, + SyncVanitiesCollectorTest.class, + AsyncVanitiesCollectorTest.class, + HttpServletRequestImpersonatorTest.class, + Task250107RemoveEsReadOnlyMonitorJobTest.class, + com.dotmarketing.business.VersionableAPITest.class, com.dotmarketing.business.UserAPITest.class, - com.dotcms.rest.api.v1.announcements.AnnouncementsHelperIntegrationTest.class, - com.dotmarketing.portlets.linkchecker.business.LinkCheckerAPITest.class, - com.dotcms.contenttype.business.FileAssetBaseTypeToContentTypeStrategyImplTest.class, - com.dotcms.publishing.remote.RemoteReceiverLanguageResolutionTest.class, - com.dotmarketing.portlets.structure.model.ContentletRelationshipsTest.class, - com.dotmarketing.portlets.cmsmaintenance.factories.CMSMaintenanceFactoryTest.class, - com.dotcms.rest.api.v1.content.search.strategies.GlobalSearchAttributeStrategyMatchingTest.class, - com.dotmarketing.portlets.contentlet.model.IntegrationResourceLinkTest.class, - com.dotcms.ai.viewtool.AIViewToolTest.class, - com.dotcms.telemetry.collectors.experiment.CountVariantsInAllEndedExperimentsMetricTypeTest.class, - com.dotmarketing.startup.runonce.Task250604UpdateFolderInodesTest.class, - com.dotcms.rendering.velocity.viewtools.DotTemplateToolTest.class, - com.dotmarketing.startup.runonce.Task220330ChangeVanityURLSiteFieldTypeTest.class, - com.dotcms.rendering.velocity.viewtools.ContentSearchToolTest.class, - com.dotcms.rendering.velocity.viewtools.XsltToolTest.class, - com.dotcms.telemetry.collectors.theme.TotalSizeOfFilesPerThemeMetricTypeTest.class, - com.dotcms.contenttype.test.FieldBuilderTest.class, - com.dotcms.rest.api.v1.drive.ContentDriveWorkflowFilterTest.class, - com.dotcms.rest.api.v1.maintenance.ClusterLogCollectorTest.class, - com.dotmarketing.portlets.workflows.actionlet.SaveContentActionletWithTagsTest.class, - com.dotcms.contenttype.test.ContentTypeBuilderTest.class, - com.dotmarketing.portlets.workflows.business.WorkflowAPIMultiLanguageTest.class, - com.dotcms.publisher.endpoint.bean.PublishingEndPointTest.class, - com.dotcms.integritycheckers.HostIntegrityCheckerTest.class, - com.dotmarketing.startup.runonce.Task220825CreateVariantFieldTest.class, - com.dotmarketing.startup.runonce.Task240606AddVariableColumnToWorkflowTest.class, - com.dotcms.variant.VariantFactoryTest.class, - com.dotcms.rest.WebResourceIntegrationTest.class, - com.dotcms.rest.api.v1.relationships.RelationshipsResourceTest.class, - com.dotcms.contenttype.model.field.layout.FieldLayoutColumnSerializerTest.class, - com.dotcms.saml.IdentityProviderConfigurationFactoryTest.class, - com.dotmarketing.startup.runonce.Task210816DeInodeRelationshipTest.class, - com.dotmarketing.portlets.rules.business.RulesCacheFTest.class, - com.dotcms.integritycheckers.FolderIntegrityCheckerTest.class, - com.dotcms.enterprise.publishing.remote.bundler.ContainerBundlerTest.class, - com.dotcms.publishing.manifest.ManifestReaderFactoryTest.class, - com.dotcms.publishing.manifest.CSVManifestBuilderTest.class, - com.liferay.portal.language.LanguageUtilTest.class, + com.dotmarketing.business.portal.PortletAPIImplTest.class, + com.dotmarketing.business.web.LanguageWebApiTest.class, + com.dotmarketing.business.IdentifierFactoryTest.class, + com.dotmarketing.business.IdentifierAPITest.class, + com.dotmarketing.business.CommitListenerCacheWrapperTest.class, + com.dotmarketing.business.RoleAPITest.class, + com.dotmarketing.business.IdentifierConsistencyIntegrationTest.class, + com.dotmarketing.business.LayoutAPITest.class, + com.dotmarketing.business.PermissionAPIIntegrationTest.class, + com.dotmarketing.business.PermissionAPITest.class, + com.dotmarketing.servlets.BinaryExporterServletTest.class, + com.dotmarketing.servlets.ShortyServletAndTitleImageTest.class, + com.dotmarketing.servlets.InitRunnerTest.class, + com.dotmarketing.servlets.ajax.AjaxDirectorServletIntegrationTest.class, + FocalPointAPITest.class, + com.dotmarketing.tag.business.TagAPITest.class, + OSGIUtilTest.class, + EncryptPlainPasswordsJobTest.class, + CachedParameterDecoratorTest.class, + ContainerFactoryImplTest.class, + TemplateFactoryImplTest.class, + TestConfig.class, + FolderTest.class, + PublishAuditAPITest.class, + BundleFactoryTest.class, + com.dotcms.security.apps.SecretsStoreKeyStoreImplTest.class, + AppsCacheImplTest.class, + VelocityServletIntegrationTest.class, com.dotmarketing.common.db.DotDatabaseMetaDataTest.class, - com.dotmarketing.startup.runonce.Task211101AddContentletAsJsonColumnTest.class, - com.dotcms.enterprise.publishing.remote.bundler.WorkflowBundlerTest.class, + com.dotmarketing.common.db.ParamsSetterTest.class, + com.dotmarketing.cms.urlmap.URLMapAPIImplTest.class, + com.dotmarketing.factories.PublishFactoryTest.class, + com.dotmarketing.factories.WebAssetFactoryTest.class, + com.dotmarketing.db.DbConnectionFactoryTest.class, + com.dotmarketing.db.DbConnectionFactoryUtilTest.class, + com.dotmarketing.db.HibernateUtilTest.class, com.dotmarketing.quartz.job.BinaryCleanupJobTest.class, - com.dotcms.enterprise.publishing.remote.bundler.TemplateBundlerTest.class, - com.dotcms.rendering.velocity.viewtools.JSONToolTest.class, - com.dotmarketing.startup.runonce.Task240530AddDotAIPortletToLayoutTest.class, - com.dotcms.enterprise.publishing.remote.bundler.RuleBundlerTest.class, - com.dotcms.rest.api.v1.system.ConfigurationHelperTest.class, + + com.dotmarketing.fixTasks.FixTask00085FixEmptyParentPathOnIdentifierTest.class, + com.dotmarketing.startup.runonce.Task05170DefineFrontEndAndBackEndRolesTest.class, + com.dotmarketing.startup.runonce.Task04375UpdateCategoryKeyTest.class, + com.dotmarketing.startup.runonce.Task04335CreateSystemWorkflowTest.class, + com.dotmarketing.startup.runonce.Task04375UpdateColorsTest.class, + com.dotmarketing.startup.runonce.Task05160MultiTreeAddPersonalizationColumnAndChangingPKTest.class, com.dotmarketing.startup.runonce.Task05035LanguageTableIdentityOffTest.class, - com.dotcms.publisher.bundle.business.BundleFactoryImplTest.class, - com.dotcms.rest.api.v1.apps.view.AppsInterpolationTest.class, - com.dotcms.analytics.track.RequestMatcherTest.class, - com.dotmarketing.startup.runonce.Task260720AddDefaultBaseTypeToFolderTableTest.class, - com.dotmarketing.util.ResourceCollectorUtilTest.class, - com.dotmarketing.startup.runonce.Task250107RemoveEsReadOnlyMonitorJobTest.class, - com.dotcms.mock.request.CachedParameterDecoratorTest.class, - com.dotcms.cache.lettuce.DotObjectCodecTest.class, - com.dotcms.storage.repository.HashedLocalFileRepositoryManagerTest.class, - com.dotmarketing.startup.runonce.Task240112AddMetadataColumnToStructureTableTest.class, - com.dotmarketing.util.ConfigUtilsTest.class + com.dotmarketing.startup.runonce.Task05165CreateContentTypeWorkflowActionMappingTableTest.class, + com.dotmarketing.startup.runonce.Task05070AndTask05080Test.class, + com.dotmarketing.startup.runonce.Task05030UpdateSystemContentTypesHostTest.class, + com.dotmarketing.startup.runonce.Task05050FileAssetContentTypeReadOnlyFileNameTest.class, + com.dotmarketing.startup.runonce.Task05190UpdateFormsWidgetCodeFieldTest.class, + com.dotmarketing.startup.runalways.Task00001LoadSchemaIntegrationTest.class, + com.dotmarketing.startup.runonce.Task05200WorkflowTaskUniqueKeyTest.class, + Task05195CreatesDestroyActionAndAssignDestroyDefaultActionsToTheSystemWorkflowTest.class, + Task05210CreateDefaultDotAssetTest.class, + DotAssetAPITest.class, + DotAssetBaseTypeToContentTypeStrategyImplTest.class, + FileAssetAPIImplIntegrationTest.class, + FileAssetFactoryIntegrationTest.class, + UserResourceIntegrationTest.class, + IntegrationResourceLinkTest.class, + HashBuilderTest.class, + LanguageUtilTest.class, + FolderResourceTest.class, + FolderResourceSearchTest.class, + Task05225RemoveLoadRecordsToIndexTest.class, + PublisherFilterImplTest.class, + PushPublishFiltersInitializerTest.class, + PushPublishFilterResourceTest.class, + PublishingResourceIntegrationTest.class, + BundleManagementResourceIntegrationTest.class, + PushNowActionletTest.class, + Task05305AddPushPublishFilterColumnTest.class, + CMSMaintenanceFactoryTest.class, + Task05350AddDotSaltClusterColumnTest.class, + PostgresPubSubImplTest.class, + DotParseTest.class, + TestWorkflowAction.class, + SamlConfigurationServiceTest.class, + ClusterFactoryTest.class, + BundleResourceTest.class, + IdentityProviderConfigurationFactoryTest.class, + GoogleTranslationServiceIntegrationTest.class, + Task240131UpdateLanguageVariableContentTypeTest.class, + PushedAssetUtilTest.class, + OpenAIAutoTagActionletTest.class, + Task250828CreateCustomAttributeTableTest.class, + CustomAttributeAPIImplTest.class, + CustomAttributeFactoryTest.class, + PermissionResourceIntegrationTest.class, + FileAssetBaseTypeToContentTypeStrategyImplTest.class, }) -public class MainSuite2b { +public class MainSuite2b { } diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite3a.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite3a.java index 4b94a7676fc1..0e647ff26604 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite3a.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite3a.java @@ -1,109 +1,107 @@ package com.dotcms; +import com.dotcms.ai.api.OpenAIVisionAPIImplTest; +import com.dotcms.ai.util.ContentToStringUtilTest; +import com.dotcms.contenttype.business.StoryBlockValidationTest; +import com.dotcms.contenttype.test.StoryBlockUtilTest; +import com.dotcms.cost.RequestCostReportTest; +import com.dotcms.jitsu.validators.AnalyticsValidatorUtilTest; import com.dotcms.junit.MainBaseSuite; +import com.dotcms.publisher.business.PublisherQueueJobTest; +import com.dotcms.rest.api.v1.drive.ContentDriveFieldFilterTest; +import com.dotcms.rest.api.v1.drive.ContentDriveHelperContentletAPIComparisonTest; +import com.dotcms.rest.api.v1.drive.ContentDriveKeywordSearchTest; +import com.dotcms.rest.api.v1.drive.ContentDriveWorkflowArchiveStepTest; +import com.dotcms.rest.api.v1.drive.ContentDriveWorkflowFilterTest; +import com.dotcms.rest.api.v1.system.cache.CacheResourceIntegrationTest; +import com.dotcms.security.apps.AppsAPIImplTest; +import com.dotcms.telemetry.collectors.MetricTimeoutTest; +import com.dotcms.telemetry.collectors.experiment.CountPagesWithAllEndedExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountPagesWithArchivedExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountPagesWithDraftExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountPagesWithRunningExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountPagesWithScheduledExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllArchivedExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllDraftExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllEndedExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllRunningExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.experiment.CountVariantsInAllScheduledExperimentsMetricTypeTest; +import com.dotcms.telemetry.collectors.theme.TotalSizeOfFilesPerThemeMetricTypeTest; +import com.dotcms.util.TimeMachineUtilTest; +import com.dotmarketing.business.DeterministicIdentifierAPITest; +import com.dotmarketing.business.SecondaryCategoryPermissionTest; +import com.dotmarketing.factories.TreeFactoryTest; +import com.dotmarketing.fixtask.tasks.FixTask00090RecreateMissingFoldersInParentPathTest; +import com.dotmarketing.portlets.contentlet.action.ImportContentletsActionSmokeTest; +import com.dotmarketing.portlets.rules.RuleAPITest; +import com.dotmarketing.startup.runonce.Task230630CreateRunningIdsExperimentFieldIntegrationTest; +import com.dotmarketing.startup.runonce.Task250604UpdateFolderInodesTest; +import com.dotmarketing.startup.runonce.Task250826AddIndexesToUniqueFieldsTableTest; +import com.dotmarketing.startup.runonce.Task251103AddStylePropertiesColumnInMultiTreeTest; +import com.dotmarketing.startup.runonce.Task251212AddVersionColumnIndicesTableTest; +import com.dotmarketing.startup.runonce.Task260206AddUsagePortletToMenuTest; +import com.dotmarketing.startup.runonce.Task260320AddPluginsPortletToMenuTest; +import com.dotmarketing.startup.runonce.Task260407AddBaseTypeColumnToIdentifierTest; +import com.dotmarketing.startup.runonce.Task260505AddPluginsPortletToMenuTest; +import com.dotmarketing.startup.runonce.Task260615AlterClusterIdLengthTest; import org.junit.runner.RunWith; -import org.junit.runners.Suite.SuiteClasses; +import org.junit.runners.Suite; -/** - * Integration test suite shard 5 of 7. - * - * Shards are balanced on measured per-class test time so the slowest shard - * bounds the CI critical path as tightly as possible. When adding a test, - * put it in the shard with the lowest total time rather than appending here - * by habit - see .github/test-matrix.yml for the shard list. - * - * Classes are fully qualified so that rebalancing does not churn imports. - */ @RunWith(MainBaseSuite.class) -@SuiteClasses({ - - // Data-scanning tests run FIRST on purpose. - // Integration tests accumulate content and never clean up, so anything - // that walks the whole dataset (executeUpgrade, findAll*) costs - // O(all content created so far). Scheduled late these pay for every - // preceding test's leftovers. Keep new full-scan tests in this block. - com.dotmarketing.portlets.containers.business.ContainerAPIImplTest.class, - com.dotcms.ema.EMAWebInterceptorTest.class, - - com.dotmarketing.portlets.contentlet.business.ContentletAPITest.class, - com.dotcms.experiments.business.ExperimentAPIImpIntegrationTest.class, - com.dotcms.util.content.json.PopulateContentletAsJSONUtilTest.class, +@Suite.SuiteClasses({ + RuleAPITest.class, + DeterministicIdentifierAPITest.class, + CountPagesWithAllEndedExperimentsMetricTypeTest.class, + CountPagesWithArchivedExperimentsMetricTypeTest.class, + CountPagesWithDraftExperimentsMetricTypeTest.class, + CountPagesWithRunningExperimentsMetricTypeTest.class, + CountPagesWithScheduledExperimentsMetricTypeTest.class, + CountVariantsInAllArchivedExperimentsMetricTypeTest.class, + CountVariantsInAllDraftExperimentsMetricTypeTest.class, + CountVariantsInAllEndedExperimentsMetricTypeTest.class, + CountVariantsInAllRunningExperimentsMetricTypeTest.class, + CountVariantsInAllScheduledExperimentsMetricTypeTest.class, + MetricTimeoutTest.class, + Task230630CreateRunningIdsExperimentFieldIntegrationTest.class, + TotalSizeOfFilesPerThemeMetricTypeTest.class, + TimeMachineUtilTest.class, + Task250604UpdateFolderInodesTest.class, + FixTask00090RecreateMissingFoldersInParentPathTest.class, + AnalyticsValidatorUtilTest.class, + Task250826AddIndexesToUniqueFieldsTableTest.class, + SecondaryCategoryPermissionTest.class, + RequestCostReportTest.class, + OpenAIVisionAPIImplTest.class, + ContentDriveFieldFilterTest.class, + ContentDriveHelperContentletAPIComparisonTest.class, + ContentDriveKeywordSearchTest.class, + ContentDriveWorkflowArchiveStepTest.class, + ContentDriveWorkflowFilterTest.class, + AppsAPIImplTest.class, + com.dotcms.content.elasticsearch.business.ESContentletAPIImplTest.class, + com.dotcms.rendering.velocity.viewtools.content.util.ContentUtilsTest.class, + com.dotcms.browser.BrowserAPITest.class, + com.dotcms.rest.api.v1.content.search.strategies.GlobalSearchAttributeStrategyMatchingTest.class, + com.dotcms.contenttype.test.ContentResourceTest.class, + com.dotmarketing.portlets.htmlpages.business.render.HTMLPageAssetRenderedAPIImplIntegrationTest.class, + com.dotcms.contenttype.business.ContentTypeDestroyAPIImplTest.class, com.dotcms.rest.api.v1.apps.AppsResourceTest.class, - com.dotmarketing.quartz.DotStatefulJobTest.class, - com.dotmarketing.startup.runonce.Task05380ChangeContainerPathToAbsoluteTest.class, - com.dotcms.uuid.shorty.ShortyIdApiTest.class, - com.dotcms.rest.api.v1.taillog.TailLogResourceTest.class, - com.dotmarketing.portlets.containers.business.ContainerAPITest.class, - com.dotmarketing.portlets.contentlet.transform.ContentletTransformerTest.class, - com.dotmarketing.factories.PublishFactoryTest.class, - com.dotmarketing.portlets.structure.factories.StructureFactoryTest.class, - com.dotcms.rest.BundleResourceTest.class, - com.dotmarketing.servlets.BinaryExporterServletTest.class, - com.dotcms.enterprise.publishing.bundler.URLMapBundlerTest.class, - com.dotcms.rest.api.v1.vtl.VTLResourceIntegrationTest.class, - com.dotcms.filters.VanityUrlFilterTest.class, - com.dotmarketing.portlets.fileassets.business.FileAssetFactoryIntegrationTest.class, - com.dotcms.integritycheckers.IntegrityUtilTest.class, - com.dotcms.concurrent.lock.DotKeyLockManagerTest.class, - com.dotcms.rest.StoryBlockMarkdownPopulatorTest.class, - com.dotmarketing.portlets.workflows.actionlet.FourEyeApproverActionletTest.class, - com.dotmarketing.business.DeterministicIdentifierAPITest.class, - com.dotmarketing.portlets.folders.business.FolderFactoryImplTest.class, - com.dotcms.translate.GoogleTranslationServiceIntegrationTest.class, - com.dotcms.util.pagination.ContentTypesPaginatorTest.class, - com.dotcms.rest.api.v1.drive.ContentDriveKeywordSearchTest.class, - com.dotcms.telemetry.collectors.experiment.CountVariantsInAllArchivedExperimentsMetricTypeTest.class, - com.dotcms.telemetry.collectors.experiment.CountPagesWithScheduledExperimentsMetricTypeTest.class, - com.dotcms.content.elasticsearch.business.ESIndexSpeedTest.class, - com.dotcms.telemetry.collectors.experiment.CountPagesWithAllEndedExperimentsMetricTypeTest.class, - com.dotcms.content.model.hydration.MetadataDelegateTest.class, - com.dotcms.rendering.velocity.viewtools.content.StoryBlockMapTest.class, - com.dotcms.graphql.datafetcher.CategoryFieldDataFetcherTest.class, - com.dotmarketing.portlets.workflows.actionlet.SaveContentAsDraftActionletIntegrationTest.class, - com.dotcms.ai.workflow.OpenAIAutoTagActionletTest.class, - com.dotcms.csspreproc.CSSPreProcessServletIT.class, - com.dotmarketing.portlets.links.factories.LinkFactoryTest.class, - com.dotmarketing.portlets.links.business.MenuLinkAPITest.class, - com.dotmarketing.sitesearch.viewtool.SiteSearchWebAPITest.class, - com.dotcms.enterprise.publishing.remote.handler.ContentHandlerTest.class, - com.liferay.portal.ejb.UserLocalManagerTest.class, - com.dotmarketing.startup.runonce.Task05190UpdateFormsWidgetCodeFieldTest.class, - com.dotcms.contenttype.test.ContentTypeTest.class, - com.dotcms.rendering.velocity.viewtools.LanguageWebAPITest.class, - com.dotcms.auth.providers.saml.v1.DotSamlResourceTest.class, - com.dotcms.rest.api.v1.announcements.RemoteAnnouncementsLoaderIntegrationTest.class, - com.dotcms.contenttype.model.field.layout.FieldLayoutSerializerTest.class, - com.liferay.util.LocaleUtilTest.class, - com.dotcms.publisher.receiver.BundlePublisherTest.class, - com.dotcms.enterprise.publishing.remote.bundler.HostBundlerTest.class, - com.dotcms.security.apps.SecretsStoreKeyStoreImplTest.class, - com.dotcms.analytics.metrics.QueryParameterValuesTransformerTest.class, - com.dotmarketing.portlets.rules.RuleAPITest.class, - com.dotmarketing.startup.runonce.Task250113CreatePostgresJobQueueTablesTest.class, - com.dotcms.enterprise.publishing.remote.handler.HandlerUtilTest.class, - com.dotmarketing.startup.runonce.Task221007AddVariantIntoPrimaryKeyTest.class, - com.dotcms.cache.lettuce.LettuceCacheTest.class, - com.dotmarketing.startup.runonce.Task220203RemoveFolderInodeConstraintTest.class, - com.dotmarketing.startup.runonce.Task05165CreateContentTypeWorkflowActionMappingTableTest.class, - com.dotmarketing.startup.runonce.Task05070AndTask05080Test.class, - com.dotmarketing.startup.runonce.Task240131UpdateLanguageVariableContentTypeTest.class, - com.dotmarketing.startup.runonce.Task201013AddNewColumnsToIdentifierTableTest.class, - com.dotmarketing.startup.runonce.Task04375UpdateCategoryKeyTest.class, - com.dotmarketing.startup.runonce.Task260206AddUsagePortletToMenuTest.class, - com.dotmarketing.portlets.rules.conditionlet.UsersBrowserLanguageConditionletTest.class, - com.dotmarketing.startup.runonce.Task230713IncreaseDisabledWysiwygColumnSizeTest.class, - com.dotcms.rest.api.v3.contenttype.MoveFieldFormTest.class, - com.dotmarketing.startup.runonce.Task220401CreateClusterLockTableTest.class, - com.dotcms.variant.business.VariantCacheTest.class, - com.dotmarketing.startup.runonce.Task05050FileAssetContentTypeReadOnlyFileNameTest.class, - com.dotcms.cdi.SimpleInjectionIT.class, - com.dotmarketing.portlets.rules.conditionlet.UsersSiteVisitsConditionletTest.class, - com.dotcms.cdi.SimpleJUnit4InjectionIT.class, - com.dotmarketing.util.TestConfig.class, - com.dotmarketing.startup.runonce.Task05390MakeRoomForLongerJobDetailTest.class, - com.dotmarketing.util.HashBuilderTest.class, - com.dotcms.rest.api.v1.system.cache.CacheResourceIntegrationTest.class + Task251103AddStylePropertiesColumnInMultiTreeTest.class, + StoryBlockValidationTest.class, + StoryBlockUtilTest.class, + Task251212AddVersionColumnIndicesTableTest.class, + Task260206AddUsagePortletToMenuTest.class, + Task260320AddPluginsPortletToMenuTest.class, + Task260505AddPluginsPortletToMenuTest.class, + Task260407AddBaseTypeColumnToIdentifierTest.class, + Task260615AlterClusterIdLengthTest.class, + ImportContentletsActionSmokeTest.class, + TreeFactoryTest.class, + PublisherQueueJobTest.class, + ContentToStringUtilTest.class, + CacheResourceIntegrationTest.class, }) + public class MainSuite3a { } diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite3b.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite3b.java deleted file mode 100644 index 7a16e3736922..000000000000 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite3b.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.dotcms; - -import com.dotcms.junit.MainBaseSuite; -import org.junit.runner.RunWith; -import org.junit.runners.Suite.SuiteClasses; - -/** - * Integration test suite shard 6 of 7. - * - * Shards are balanced on measured per-class test time so the slowest shard - * bounds the CI critical path as tightly as possible. When adding a test, - * put it in the shard with the lowest total time rather than appending here - * by habit - see .github/test-matrix.yml for the shard list. - * - * Classes are fully qualified so that rebalancing does not churn imports. - */ -@RunWith(MainBaseSuite.class) -@SuiteClasses({ - - // Data-scanning tests run FIRST on purpose. - // Integration tests accumulate content and never clean up, so anything - // that walks the whole dataset (executeUpgrade, findAll*) costs - // O(all content created so far). Scheduled late these pay for every - // preceding test's leftovers. Keep new full-scan tests in this block. - com.dotmarketing.portlets.templates.business.TemplateAPITest.class, - - com.dotcms.content.elasticsearch.business.ESContentletAPIImplTest.class, - com.dotcms.rendering.velocity.services.HTMLPageAssetRenderedTest.class, - com.dotcms.content.elasticsearch.business.ESIndexAPITest.class, - com.dotmarketing.portlets.folders.business.FolderAPITest.class, - com.dotmarketing.cms.urlmap.URLMapAPIImplTest.class, - com.dotcms.storage.FileMetadataAPITest.class, - com.dotmarketing.portlets.htmlpages.business.HTMLPageAPITest.class, - com.dotmarketing.portlets.languagesmanager.business.LanguageAPITest.class, - com.dotmarketing.portlets.containers.business.FileAssetContainerUtilTest.class, - com.dotmarketing.portlets.browser.BrowserUtilTest.class, - com.dotmarketing.business.VersionableAPITest.class, - com.dotcms.content.business.json.ContentletJsonAPITest.class, - com.dotcms.enterprise.priv.ESSearchProxyTest.class, - com.dotcms.enterprise.publishing.PublishDateUpdaterIntegrationTest.class, - com.dotmarketing.business.PermissionAPIIntegrationTest.class, - com.dotcms.rest.api.v2.asset.WebAssetResourceV2IntegrationTest.class, - com.dotcms.rest.api.v1.system.permission.PermissionResourceIntegrationTest.class, - com.dotmarketing.business.VersionableFactoryImplTest.class, - com.dotcms.rest.api.v1.contenttype.ContentTypeResourceTest.class, - com.dotmarketing.portlets.contentlet.business.ContentletFactoryTest.class, - com.dotmarketing.portlets.templates.business.FileAssetTemplateUtilTest.class, - com.dotmarketing.filters.CMSUrlUtilIntegrationTest.class, - com.dotcms.rest.api.v3.contenttype.FieldResourceTest.class, - com.dotcms.rest.api.v1.contenttype.ContentTypeResourceIssue15124Test.class, - com.dotcms.graphql.datafetcher.page.NumberContentsDataFetcherTest.class, - com.dotcms.analytics.track.collectors.PagesCollectorTest.class, - com.dotcms.contenttype.test.DeleteFieldJobTest.class, - com.dotcms.contenttype.test.JsonContentTypeTransformerTest.class, - com.dotcms.telemetry.collectors.experiment.CountPagesWithRunningExperimentsMetricTypeTest.class, - com.dotcms.telemetry.collectors.experiment.CountVariantsInAllScheduledExperimentsMetricTypeTest.class, - com.dotcms.contenttype.business.SiteAndFolderResolverImplTest.class, - com.dotmarketing.portlets.workflows.business.SaveContentActionletTest.class, - com.dotcms.ai.viewtool.EmbeddingsToolTest.class, - com.dotcms.ai.app.ConfigServiceTest.class, - com.dotcms.rendering.velocity.viewtools.WebsiteToolTest.class, - com.dotmarketing.osgi.GenericBundleActivatorIntegrationTest.class, - com.dotmarketing.portlets.containers.business.ContainerStructureFinderStrategyResolverTest.class, - com.dotcms.publisher.bundle.business.BundleAPITest.class, - com.dotcms.publisher.bundle.business.BundleFactoryTest.class, - com.dotmarketing.factories.WebAssetFactoryTest.class, - com.dotmarketing.portlets.containers.business.ContainerFactoryImplTest.class, - com.dotcms.rest.api.v1.container.ContainerResourceIntegrationTest.class, - com.dotcms.auth.providers.jwt.JsonWebTokenUtilsIntegrationTest.class, - com.dotcms.ai.viewtool.CompletionsToolTest.class, - com.dotcms.enterprise.publishing.remote.handler.RuleBundlerHandlerTest.class, - com.dotmarketing.portlets.workflows.business.WorkflowFactoryTest.class, - com.dotcms.contenttype.model.field.layout.FieldLayoutTest.class, - com.dotcms.rest.api.v1.asset.AssetPathResolverImplIntegrationTest.class, - com.dotmarketing.portlets.rules.conditionlet.VisitedUrlConditionletTest.class, - com.dotcms.analytics.attributes.CustomAttributeAPIImplTest.class, - com.dotcms.publisher.endpoint.business.PublishingEndPointAPITest.class, - com.dotmarketing.portlets.workflows.model.SystemActionWorkflowActionMappingTest.class, - com.dotcms.publishing.PushPublishFiltersInitializerTest.class, - com.dotcms.rendering.velocity.services.VelocityResourceKeyTest.class, - com.dotcms.cluster.business.ServerAPIImplTest.class, - com.dotcms.enterprise.publishing.remote.bundler.ContentTypeBundlerTest.class, - com.dotmarketing.common.db.DBTimeZoneCheckTest.class, - com.dotmarketing.factories.TreeFactoryTest.class, - com.dotmarketing.startup.runonce.Task220202RemoveFKStructureFolderConstraintTest.class, - com.dotmarketing.startup.runonce.Task05395RemoveEndpointIdForeignKeyInIntegrityResolverTablesIntegrationTest.class, - com.dotmarketing.startup.runonce.Task220215MigrateDataFromInodeToFolderTest.class, - com.dotcms.mail.MailAPIImplTest.class, - com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionletAbortTest.class, - com.dotmarketing.startup.runonce.Task230701AddHashIndicesToWorkflowTablesTest.class, - com.dotmarketing.startup.runonce.Task220413IncreasePublishedPushedAssetIdColTest.class, - com.dotmarketing.startup.runonce.Task220912UpdateCorrectShowOnMenuPropertyTest.class, - com.dotmarketing.startup.runonce.Task250828CreateCustomAttributeTableTest.class, - com.dotcms.util.marshal.MarshalUtilsIntegrationTest.class, - com.dotmarketing.startup.runonce.Task220402UpdateDateTimezonesTest.class, - com.dotmarketing.startup.runonce.Task05350AddDotSaltClusterColumnTest.class, - com.dotcms.rest.AuditPublishingResourceTest.class, - com.dotmarketing.startup.runonce.Task241015ReplaceLanguagesWithLocalesPortletTest.class, - com.dotmarketing.startup.runonce.Task210805DropUserProxyTableTest.class, - com.dotmarketing.beans.HostTest.class, - com.dotmarketing.startup.runonce.Task211007RemoveNotNullConstraintFromCompanyMXColumnTest.class, - com.dotmarketing.common.db.DotConnectTest.class, - com.dotmarketing.startup.runonce.Task04375UpdateColorsTest.class, - com.dotmarketing.db.DbConnectionFactoryUtilTest.class -}) -public class MainSuite3b { - -} diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite4a.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite4a.java deleted file mode 100644 index 2c3ebfe31712..000000000000 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite4a.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.dotcms; - -import com.dotcms.junit.MainBaseSuite; -import org.junit.runner.RunWith; -import org.junit.runners.Suite.SuiteClasses; - -/** - * Integration test suite shard 7 of 7. - * - * Shards are balanced on measured per-class test time so the slowest shard - * bounds the CI critical path as tightly as possible. When adding a test, - * put it in the shard with the lowest total time rather than appending here - * by habit - see .github/test-matrix.yml for the shard list. - * - * Classes are fully qualified so that rebalancing does not churn imports. - */ -@RunWith(MainBaseSuite.class) -@SuiteClasses({ - - // Data-scanning tests run FIRST on purpose. - // Integration tests accumulate content and never clean up, so anything - // that walks the whole dataset (executeUpgrade, findAll*) costs - // O(all content created so far). Scheduled late these pay for every - // preceding test's leftovers. Keep new full-scan tests in this block. - com.dotcms.content.elasticsearch.util.ESMappingUtilHelperTest.class, - - com.dotcms.contenttype.test.ContentTypeAPIImplTest.class, - com.dotmarketing.portlets.htmlpages.business.render.HTMLPageAssetRenderedAPIImplIntegrationTest.class, - com.dotcms.contenttype.test.ContentTypeFactoryImplTest.class, - com.dotcms.publisher.util.DependencyModDateUtilTest.class, - com.dotmarketing.portlets.workflows.business.WorkflowAPITest.class, - com.dotmarketing.tag.business.TagAPITest.class, - com.dotcms.rest.api.v1.workflow.WorkflowResourceIntegrationTest.class, - com.dotcms.rendering.velocity.viewtools.navigation.NavToolTest.class, - com.dotcms.rest.api.v1.contenttype.FieldResourceTest.class, - com.dotcms.variant.VariantAPITest.class, - com.dotcms.rendering.velocity.servlet.VelocityServletIntegrationTest.class, - com.dotcms.jobs.business.api.JobQueueManagerAPITest.class, - com.dotcms.contenttype.business.StoryBlockValidationTest.class, - com.dotcms.dotpubsub.PostgresPubSubImplTest.class, - com.dotmarketing.portlets.categories.business.CategoryAPITest.class, - com.dotcms.telemetry.collectors.MetricTimeoutTest.class, - com.dotcms.rest.api.v1.publishing.BundleManagementResourceIntegrationTest.class, - com.dotmarketing.business.IdentifierFactoryTest.class, - com.dotcms.rendering.velocity.directive.DotParseTest.class, - com.dotmarketing.portlets.contentlet.ajax.ContentletAjaxTest.class, - com.dotcms.ai.listener.EmbeddingContentListenerTest.class, - com.dotcms.rest.api.v1.content.ContentVersionResourceIntegrationTest.class, - com.dotcms.rest.api.v1.authentication.ResetPasswordResourceIntegrationTest.class, - com.dotcms.rest.TagResourceIntegrationTest.class, - com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionletTest.class, - com.dotmarketing.portlets.categories.business.CategoryFactoryTest.class, - com.dotcms.rest.api.v1.page.NavResourceTest.class, - com.dotcms.publishing.PublisherAPITest.class, - com.dotcms.telemetry.collectors.experiment.CountPagesWithArchivedExperimentsMetricTypeTest.class, - com.dotcms.publisher.business.PublisherQueueJobTest.class, - com.dotmarketing.portlets.workflows.actionlet.PushNowActionletTest.class, - com.dotmarketing.portlets.workflows.business.SaveContentDraftActionletTest.class, - com.dotcms.rest.api.v1.apps.SiteViewPaginatorIntegrationTest.class, - com.dotcms.contenttype.business.ContentTypeInitializerTest.class, - com.dotcms.rest.api.v1.user.UserResourceIntegrationTest.class, - com.dotmarketing.business.SecondaryCategoryPermissionTest.class, - com.dotcms.storage.FileStorageAPITest.class, - com.dotcms.publisher.util.PushedAssetUtilTest.class, - com.dotcms.rest.api.v1.workflow.WorkflowResourceLicenseIntegrationTest.class, - com.dotcms.graphql.datafetcher.page.RunningExperimentFetcherTest.class, - com.dotcms.contenttype.model.field.layout.FieldUtilTest.class, - com.dotmarketing.util.MaintenanceUtilTest.class, - com.dotmarketing.portlets.contentlet.business.ContentletCacheImplTest.class, - com.dotmarketing.startup.runonce.Task230523CreateVariantFieldInContentletIntegrationTest.class, - com.dotmarketing.fixtask.tasks.FixTask00090RecreateMissingFoldersInParentPathTest.class, - com.dotcms.rest.api.v1.authentication.CreateJsonWebTokenResourceIntegrationTest.class, - com.dotcms.content.business.json.LegacyJSONObjectRenderTest.class, - com.dotcms.visitor.filter.characteristics.VisitorCharacterTest.class, - com.dotmarketing.fixTasks.FixTask00085FixEmptyParentPathOnIdentifierTest.class, - com.dotcms.rendering.velocity.viewtools.content.StoryBlockTest.class, - com.dotmarketing.business.web.UserWebAPIImplTest.class, - com.dotcms.auth.providers.jwt.services.JsonWebTokenServiceIntegrationTest.class, - com.dotmarketing.startup.runonce.Task260407AddBaseTypeColumnToIdentifierTest.class, - com.dotmarketing.quartz.QuartzUtilsTest.class, - com.dotcms.analytics.bayesian.BayesianAPIImplIT.class, - com.dotcms.rest.api.v1.configuration.ConfigurationResourceTest.class, - com.dotcms.enterprise.publishing.remote.bundler.FolderBundlerTest.class, - com.dotmarketing.portlets.personas.business.DeleteMultiTreeUsedPersonaTagJobTest.class, - com.dotmarketing.util.UtilMethodsITest.class, - com.dotmarketing.startup.runonce.Task230110MakeSomeSystemFieldsRemovableByBaseTypeTest.class, - com.dotmarketing.startup.runonce.Task201102UpdateColumnSitelicTableTest.class, - com.dotmarketing.startup.runonce.Task220822CreateVariantTableTest.class, - com.dotmarketing.startup.runonce.Task231109AddPublishDateToContentletVersionInfoTest.class, - com.dotcms.contenttype.test.StoryBlockUtilTest.class, - com.dotmarketing.startup.runonce.Task260505AddPluginsPortletToMenuTest.class, - com.dotmarketing.startup.runonce.Task230328AddMarkedForDeletionColumnTest.class, - com.dotmarketing.startup.runonce.Task260320AddPluginsPortletToMenuTest.class, - com.dotcms.filters.interceptor.meta.MetaWebInterceptorTest.class, - com.dotmarketing.startup.runonce.Task220606UpdatePushNowActionletNameTest.class, - com.dotmarketing.portlets.workflows.model.TestWorkflowAction.class, - com.dotmarketing.startup.runonce.Task211103RenameHostNameLabelTest.class, - com.dotmarketing.startup.runonce.Task241016AddCustomLanguageVariablesPortletToLayoutTest.class, - com.dotmarketing.startup.runonce.Task240111AddInodeAndIdentifierLeftIndexesTest.class, - com.dotmarketing.startup.runonce.Task210719CleanUpTitleFieldTest.class, - com.dotcms.enterprise.publishing.staticpublishing.LanguageFolderTest.class, - com.dotcms.security.multipart.SecureFileValidatorTest.class, - com.dotcms.publishing.PublisherFilterImplTest.class, - com.dotcms.enterprise.cluster.ClusterFactoryTest.class -}) -public class MainSuite4a { - -} diff --git a/dotcms-postman/config.json b/dotcms-postman/config.json index 6d9c1dd3340c..dca5b14e7a41 100644 --- a/dotcms-postman/config.json +++ b/dotcms-postman/config.json @@ -1,157 +1,85 @@ [ { - "name": "content", - "collections": [ - "Content_Resource.postman_collection", - "Containers.postman_collection", - "User_Include_Into_Experiment.postman_collection", - "Bundle_Resource.postman_collection", - "BringBack.postman_collection", - "WebDav.postman_collection", - "LangVariables.postman_collection", - "Image.postman_collection", - "PortletResource", - "Maintenance_Resource-Donwload_Log_File", - "Osgi.postman_collection", - "TempAPI.postman_collection" - ] + "name": "ai", + "collections": ["AI.postman_collection"] }, { - "name": "contenttype", + "name": "category-content", "collections": [ + "Category.postman_collection", + "ContentResourceV1.postman_collection", "ContentTypeResourceTests", - "Integrity_Checker_From_Sender.postman_collection", - "DotAsset.postman_collection", - "Visitor.postman_collection", - "Push_Publish_JWT_Token_Test.postman_collection", - "JobQueueResourceAPITests.postman_collection", - "CacheResource.postman_collection", - "UIComponents.postman_collection", - "PublishingResource.postman_collection", - "MonitorResource.postman_collection", - "ForgotPasswordResource.postman_collection", - "DateTool.postman_collection", - "Maintenance_Resource", - "ProbesResource.postman_collection" + "Content_Resource.postman_collection" ] }, { - "name": "graphql-a", - "collections": [ - "GraphQLTests", - "DotFavoritePage.postman_collection", - "JsScriptAPI.postman_collection", - "SystemTable.postman_collection", - "Scripting_Resource.postman_collection", - "PushPublishFilterResource.postman_collection", - "Apps.postman_collection", - "EMA.postman_collection" - ], - "folders": { - "GraphQLTests": [ - "Pre-Execution Requests", - "Page API" - ] - } - }, - { - "name": "graphql-b", + "name": "container", "collections": [ - "GraphQLTests", - "LanguageResourceTests", "ContainerResource.postman_collection", - "Field_Variable_Resource.postman_collection", - "Content_Version_Resource.postman_collection", - "ContentTypePages.postman_collection", - "VelocityMacro.postman_collection", - "Content_Analytics.postman_collection", - "Maintenance_Resource-Download_Starter", - "Promote_Variant.postman_collection", - "ThemeResource.postman_collection" - ], - "folders": { - "GraphQLTests": [ - "Pre-Execution Requests", - "Query Cache", - "Test BaseType fields ", - "Related content with condition / query", - "PageAPI_TestMapSpecialField", - "Test File/Image Field fields", - "Nav", - "Related content respects language in query for parent", - "Tests For New StoryBlockField", - "Get content in new Language", - "Pagination", - "Metadata ", - "Page Lock Test", - "Test Render Content Fields", - "Test DateField Render Right Format", - "Given JSONField should return as JSON", - "Cats", - "File Metadata", - "Tags", - "Empty Collection", - "Disallow Introspection Query", - "Page API - Testing 'page' field with inline fragments", - "DotFolderByPath Tests" - ] - } + "Containers.postman_collection" + ] }, { - "name": "pages", + "name": "experiment", "collections": [ - "PagesResourceTests", - "Define_Contentlets_StyleProperties.postman_collection", - "VersionableResource.postman_collection", - "Permission_Resource.postman_collection", - "Tags_Resource_V2.postman_collection", - "Browser_Resource.postman_collection", - "Reltionship_cache_in_push_publish.postman_collection", - "PPEndpointResource.postman_collection", - "DotAsset-MultiPart-TempFile.postman_collection", - "PublishQueueResource" + "Experiments_Resource.postman_collection", + "Experiment_Result.postman_collection" ] }, { - "name": "site", + "name": "graphql", + "collections": ["GraphQLTests"] + }, + { + "name": "page", + "collections": ["PagesResourceTests"] + }, + { + "name": "pp", "collections": [ - "Site_Resource.postman_collection", - "ContentResourceV1.postman_collection", - "Category.postman_collection", - "ContentDriveResource.postman_collection", + "PublishQueueResource", "Push_Publish_from_sender.postman_collection", - "AI.postman_collection", - "VanityURL.postman_collection", - "Announcements.postman_collection", - "VelocitySecrets.postman_collection", - "ContentImportResource.postman_collection", - "Logger_Resource.postman_collection", - "Accessibility_Checker_Tests.postman_collection" + "Push_Publish_JWT_Token_Test.postman_collection", + "PushPublishFilterResource.postman_collection" ] }, { "name": "template", - "collections": [ - "Template_Resource.postman_collection", - "ApiToken_Resource.postman_collection", - "NavResourceTests", - "UserResource.postman_collection", - "Integrity_Checker_JWT_Token_Test.postman_collection", - "Save_Layout_With_Relative_Path.postman_collection", - "Content_Report_Resource.postman_collection", - "EnvironmentResource.postman_collection", - "Form_Resource.postman_collection", - "Experiment_Result.postman_collection", - "RoleResource.postman_collection" - ] + "collections": ["Template_Resource.postman_collection"] }, { "name": "workflow", + "collections": ["Workflow_Resource_Tests"] + }, + { + "name": "default-split", "collections": [ - "Workflow_Resource_Tests", - "ConfigurationResource.postman_collection", + "ApiToken_Resource.postman_collection", + "ContentImportResource.postman_collection", + "Manifest_Download_End_Point.postman_collection", + "Osgi.postman_collection", + "Page_Version_with_different_Templates.postman_collection", + "Permission_Resource.postman_collection", + "Promote_Variant.postman_collection", + "Relationship_cache_in_push_publish.postman_collection", "ResourceLink.postman_collection", - "System.postman_collection" + "RoleResource.postman_collection", + "Scripting_Resource.postman_collection", + "Site_Resource.postman_collection", + "System.postman_collection", + "SystemTable.postman_collection", + "Tags_Resource_V2.postman_collection", + "TempAPI.postman_collection", + "ThemeResource.postman_collection", + "ToolGroupResource.postman_collection", + "UIComponents.postman_collection", + "UserResource.postman_collection", + "User_Include_Into_Experiment.postman_collection", + "VelocityMacro.postman_collection", + "VelocitySecrets.postman_collection", + "VersionableResource.postman_collection", + "Visitor.postman_collection", + "WebAssets.postman_collection" ] } ] diff --git a/dotcms-postman/index.js b/dotcms-postman/index.js index e98622147dd8..a084896f2dff 100644 --- a/dotcms-postman/index.js +++ b/dotcms-postman/index.js @@ -145,8 +145,7 @@ async function runNewman( collectionName, postmanTestsDir, postmanTestsResultsDir, - jwt, - folders + jwt ) { return new Promise((resolve, reject) => { const collectionPath = path.join(postmanTestsDir, `${collectionName}.json`); @@ -157,9 +156,6 @@ async function runNewman( console.log("Running collection:", collectionName); console.log("using jwt:", jwt); - if (folders && folders.length) { - console.log("restricted to folders:", folders.join(", ")); - } // Validate and sanitize environment variables const envVars = [ @@ -170,11 +166,6 @@ async function runNewman( // Add additional configuration for Node.js 22 compatibility const newmanConfig = { collection: require(collectionPath), - // When a group pins `folders`, run only those top-level folders. Lets a - // single expensive collection be sharded across CI jobs without splitting - // the collection file. Newman preserves collection order, so a shared - // setup folder listed here still runs before the rest. - ...(folders && folders.length ? { folder: folders } : {}), envVar: envVars, reporters: ["junit", "cli"], reporter: { @@ -291,8 +282,6 @@ async function processCollections( console.log(`Starting collections for groupname: ${groupname}`); let collectionsToRun = []; - // Optional per-collection folder restriction, keyed by collection name. - let folderMap = {}; const collectionFile = path.join(postmanTestsDir, groupname + ".json"); if (fs.existsSync(collectionFile)) { @@ -316,36 +305,12 @@ async function processCollections( const configItem = config.find((item) => item.name === groupname); if (configItem) { collectionsToRun = configItem.collections; - folderMap = configItem.folders || {}; } else { console.error(`Collection or groupname '${groupname}' not found.`); process.exit(1); } } - // Validate pinned folders up front. A folder name that does not exist makes - // newman run zero requests and still exit green, so a typo would silently - // delete test coverage. Treated as a config error like an unknown groupname: - // fail immediately rather than let the run report success. - for (const [collection, folders] of Object.entries(folderMap)) { - if (!collectionsToRun.includes(collection)) { - console.error( - `Group '${groupname}' pins folders for '${collection}', which it does not run.` - ); - process.exit(1); - } - const doc = require(path.join(postmanTestsDir, `${collection}.json`)); - const known = new Set((doc.item || []).filter((i) => i.item).map((i) => i.name)); - const unknown = folders.filter((f) => !known.has(f)); - if (unknown.length) { - console.error( - `Collection '${collection}' has no folder(s): ${unknown.join(", ")}\n` + - `Known folders: ${[...known].join(" | ")}` - ); - process.exit(1); - } - } - // Run Newman for each collection and track failures for (let collection of collectionsToRun) { try { @@ -354,8 +319,7 @@ async function processCollections( collection, postmanTestsDir, postmanTestsResultsDir, - jwt, - folderMap[collection] + jwt ); console.log(`Collection ${collection} executed successfully.`); } catch (error) { diff --git a/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json b/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json index 355a57d8ffa0..fd9694047dfd 100644 --- a/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json +++ b/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json @@ -4,7 +4,6 @@ "name": "ContentType Resource", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "5403727" - }, "item": [ { @@ -1867,66 +1866,6 @@ { "name": "Test Get ContentTypes", "item": [ - { - "name": "Ensure Video DotAsset type exists (setup)", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Setup: the 'ensure' tests in this folder expect a 'Video' content type to exist.", - "// Nothing in dotCMS creates it eagerly at startup, so when this collection runs", - "// without the collections that create it indirectly, the assertions fail.", - "// Create it if missing so this collection is self-contained (400 = already exists).", - "pm.test(\"Video type exists or was created\", function () {", - " pm.expect([200, 201, 400]).to.include(pm.response.code);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "admin", - "type": "string" - }, - { - "key": "username", - "value": "admin@dotcms.com", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"clazz\": \"com.dotcms.contenttype.model.type.ImmutableDotAssetContentType\",\n \"defaultType\": false,\n \"fixed\": false,\n \"system\": false,\n \"folder\": \"SYSTEM_FOLDER\",\n \"name\": \"Video\",\n \"variable\": \"Video\",\n \"workflow\": [\n \"d61a59e1-a49c-46f2-a929-db2b4bfa88b2\"\n ]\n}" - }, - "url": { - "raw": "{{serverURL}}/api/v1/contenttype", - "host": [ - "{{serverURL}}" - ], - "path": [ - "api", - "v1", - "contenttype" - ] - } - }, - "response": [] - }, { "name": "Get ContentTypes sending HostID", "event": [ @@ -15953,4 +15892,4 @@ } } ] -} +} \ No newline at end of file diff --git a/dotcms-postman/verify-config.js b/dotcms-postman/verify-config.js deleted file mode 100644 index bf21b310e577..000000000000 --- a/dotcms-postman/verify-config.js +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env node -/** - * Sanity-checks dotcms-postman/config.json against what is actually on disk. - * - * Catches the failure modes that are otherwise silent in CI: - * - a collection listed in a group that has no .json file (group errors out) - * - a `folders` entry naming a folder that does not exist in the collection - * (newman runs ZERO requests and still exits green) - * - a collection claimed by two groups (runs twice, wastes a shard) - * - `folders` shards of one collection that do not cover every folder - * - * Run: node dotcms-postman/verify-config.js - */ -const fs = require("fs"); -const path = require("path"); - -const DIR = path.join(__dirname, "src/main/resources/postman"); -const config = JSON.parse(fs.readFileSync(path.join(__dirname, "config.json"))); - -const onDisk = new Set( - fs.readdirSync(DIR) - .filter((f) => f.endsWith(".json") && f !== "postman_environment.json") - .map((f) => f.replace(/\.json$/, "")) -); - -const errors = []; -const owner = new Map(); // collection -> [group, ...] -const folderShards = new Map(); // collection -> [[folders], ...] - -for (const group of config) { - if (!group.name) errors.push("a group is missing `name`"); - for (const coll of group.collections || []) { - if (!onDisk.has(coll)) { - errors.push(`[${group.name}] collection not on disk: ${coll}`); - continue; - } - if (!owner.has(coll)) owner.set(coll, []); - owner.get(coll).push(group.name); - - const folders = (group.folders || {})[coll]; - if (!folders) continue; - - const doc = JSON.parse(fs.readFileSync(path.join(DIR, `${coll}.json`))); - const real = new Set(doc.item.filter((i) => i.item).map((i) => i.name)); - for (const f of folders) { - if (!real.has(f)) { - errors.push( - `[${group.name}] ${coll}: folder "${f}" does not exist ` + - `(newman would run zero requests). Known: ${[...real].join(" | ")}` - ); - } - } - if (!folderShards.has(coll)) folderShards.set(coll, { real, shards: [] }); - folderShards.get(coll).shards.push(folders); - } -} - -// A collection may legitimately appear in >1 group ONLY when each appearance -// pins a disjoint set of folders. -for (const [coll, groups] of owner) { - if (groups.length === 1) continue; - const entry = folderShards.get(coll); - if (!entry || entry.shards.length !== groups.length) { - errors.push(`${coll} is claimed by ${groups.join(", ")} without folder pinning`); - continue; - } - const counts = new Map(); - for (const s of entry.shards) { - for (const f of s) counts.set(f, (counts.get(f) || 0) + 1); - } - // The shared setup folder is expected in every shard; anything else repeated - // means real duplicated work. - const dupes = [...counts].filter(([f, n]) => n > 1 && n !== entry.shards.length); - if (dupes.length) { - errors.push(`${coll}: folders in some-but-not-all shards: ${dupes.map(([f]) => f).join(", ")}`); - } - const missing = [...entry.real].filter((f) => !counts.has(f)); - if (missing.length) { - errors.push(`${coll}: folders covered by NO shard: ${missing.join(", ")}`); - } -} - -const listed = new Set(owner.keys()); -const fallsToDefault = [...onDisk].filter((c) => !listed.has(c)); - -console.log(`collections on disk : ${onDisk.size}`); -console.log(`explicitly grouped : ${listed.size} across ${config.length} groups`); -console.log(`-> "default" shard : ${fallsToDefault.length}`); -for (const [coll, e] of folderShards) { - console.log(`folder-sharded : ${coll} -> ${e.shards.length} shards covering ${e.real.size} folders`); -} - -if (errors.length) { - console.error(`\n${errors.length} PROBLEM(S):`); - errors.forEach((e) => console.error(" - " + e)); - process.exit(1); -} -console.log("\nconfig.json OK");