diff --git a/.github/workflows/release-and-deploy-release.yml b/.github/workflows/release-and-deploy-release.yml
new file mode 100644
index 0000000..2414a95
--- /dev/null
+++ b/.github/workflows/release-and-deploy-release.yml
@@ -0,0 +1,72 @@
+name: release and deploy
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ release:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: '0'
+ - name: Set up JDK 17
+ uses: actions/setup-java@v1
+ with:
+ java-version: 17
+ - name: Maven setup
+ uses: s4u/maven-settings-action@v3.0.0
+ with:
+ servers: |
+ [{
+ "id": "magnolia.enterprise.group",
+ "username": "${{secrets.MGNL_NEXUS_USER}}",
+ "password": "${{secrets.MGNL_NEXUS_PASS}}"
+ }]
+ - name: Maven verify
+ run: mvn verify --batch-mode
+ # Install xmllint
+ - name: Install dependencies
+ run: sudo apt-get update && sudo apt-get install libxml2-utils
+ # Set git user name and email
+ - name: Set up Git
+ run: |
+ chmod +x ci/setup-git.sh
+ ci/setup-git.sh
+ # Release, set correct versions and create tag
+ - name: Release (versioning/tag)
+ run: |
+ chmod +x ci/mvn-release.sh
+ ci/mvn-release.sh
+
+ deploy-release:
+
+ needs: release
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ ref: 'main'
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 17
+ server-id: central
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ - name: Release Maven package
+ run: mvn deploy -Pdeploy
+ env:
+ MAVEN_USERNAME: ${{ secrets.SONATYPE_USER }}
+ MAVEN_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
\ No newline at end of file
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
new file mode 100644
index 0000000..a049e99
--- /dev/null
+++ b/.github/workflows/verify.yml
@@ -0,0 +1,29 @@
+name: verify
+
+on:
+ push:
+ branches-ignore:
+ - main
+
+jobs:
+ verify:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 17
+ - name: Maven setup
+ uses: s4u/maven-settings-action@v3.0.0
+ with:
+ servers: |
+ [{
+ "id": "magnolia.enterprise.group",
+ "username": "${{secrets.MGNL_NEXUS_USER}}",
+ "password": "${{secrets.MGNL_NEXUS_PASS}}"
+ }]
+ - name: Maven verify
+ run: mvn verify --batch-mode
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5ff6309
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a2d7712
--- /dev/null
+++ b/README.md
@@ -0,0 +1,135 @@
+# Magnolia Testing
+
+The spring renderer module makes the Spring-Framework available for Magnolia.
+Recommended to be used with [dynamic builders modules](https://github.com/merkle-open/magnolia-dynamic-builders).
+
+## Requirements
+* Java 17
+* Magnolia >= 6.3
+
+## Setup
+
+- Add Maven dependency:
+ ```xml
+
+ com.merkle.oss.magnolia
+ magnolia-testing
+ 0.0.1
+
+ ```
+- Add a magnolia module descriptor in your `src/test/resources/META-INF/magnolia` directory (Bindings can differ from non-test setup).
+
+
+### [Integration Test](src/test/java/com/merkle/oss/magnolia/testing/SampleIntegrationTest.java)
+Creates guice context and starts all magnolia modules. Can import jcr exports (xml files) into repository using [@Repository](src/main/java/com/merkle/oss/magnolia/testing/repository/Repository.java) annotation.
+```java
+import static io.smallrye.common.constraint.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import info.magnolia.context.MgnlContext;
+import info.magnolia.objectfactory.Components;
+import info.magnolia.repository.RepositoryConstants;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import com.merkle.oss.magnolia.testing.MagnoliaIntegrationTestExtension;
+import com.merkle.oss.magnolia.testing.repository.Repository;
+
+@ExtendWith(MagnoliaIntegrationTestExtension.class)
+class SampleIntegrationTest {
+
+ @Repository(workspaces = {@Repository.Workspace(name = RepositoryConstants.WEBSITE, xml = "jcr.xml")})
+ @Test
+ void someMethod() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession(RepositoryConstants.WEBSITE);
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some value 42!", someInterface.someMethod(node));
+ session.logout();
+ }
+}
+```
+
+### [Guice Context Test](src/test/java/com/merkle/oss/magnolia/testing/SampleGuiceContextTest.java)
+Only creates guice context, but doesn't start magnolia modules. Can import jcr exports (xml files) into repository, create workspaces and import nodeTypes using [@Repository](src/main/java/com/merkle/oss/magnolia/testing/repository/Repository.java) annotation.
+
+```java
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import info.magnolia.context.MgnlContext;
+import info.magnolia.objectfactory.Components;
+import info.magnolia.repository.RepositoryConstants;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import com.merkle.oss.magnolia.testing.MagnoliaGuiceContextTestExtension;
+import com.merkle.oss.magnolia.testing.repository.Repository;
+
+@Repository(
+ nodeTypes = { @Repository.NodeTypesDefinition(cnd = "/mgnl-nodetypes/testing-nodetypes.cnd") },
+ workspaces = { @Repository.Workspace(name = "testing", xml = "jcr-custom-nodetype.xml", create = true) }
+)
+@ExtendWith(MagnoliaGuiceContextTestExtension.class)
+class SampleGuiceContextTest {
+
+ @Test
+ void someMethod() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession("testing");
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some value 42!", someInterface.someMethod(node));
+ }
+}
+
+```
+### Custom Magnolia properties
+Custom property files can be specified using the [`@TestConfiguration`](src/main/java/com/merkle/oss/magnolia/testing/configuration/TestConfiguration.java) annotation.
+
+```java
+import com.merkle.oss.magnolia.testing.MagnoliaGuiceContextTestExtension;
+import com.merkle.oss.magnolia.testing.configuration.TestConfiguration;
+
+@TestConfiguration(magnoliaProperties = "/magnolia-testing.properties")
+@ExtendWith(MagnoliaGuiceContextTestExtension.class)
+class SampleGuiceContextTest {}
+```
+
+#### Placeholders
+- `${resource.home}` is replaced by your projects `src/test/resources` directory.
+ - e.g. `magnolia.repositories.config=${resource.home}/repositories.xml`
+- `classpath:` is replaced by any classpath resource
+ - e.g. `classpath:/repository/InMemoryJcrRepositoryConfiguration.xml`
+- `magnolia.app.rootdir` is replaced by a random generated temporary folder (for each test)
+ - e.g. `magnolia.home=${magnolia.app.rootdir}`
+
+### Test specific component bindings
+Test specific component bindings can be configured using the [`@TestConfiguration`](src/main/java/com/merkle/oss/magnolia/testing/configuration/TestConfiguration.java) annotation.
+
+```java
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import com.merkle.oss.magnolia.testing.MagnoliaIntegrationTestExtension;
+import com.merkle.oss.magnolia.testing.configuration.TestConfiguration;
+
+@ExtendWith(MagnoliaIntegrationTestExtension.class)
+class SampleGuiceContextTest {
+
+ @Test
+ @TestConfiguration(components = {
+ @TestConfiguration.Component(type = SomeInterface.class, implementation = SomeInterface.SomeOtherImplementation.class)
+ })
+ void someOtherMethod() {
+ ...
+ }
+}
+```
\ No newline at end of file
diff --git a/ci/mvn-release.sh b/ci/mvn-release.sh
new file mode 100755
index 0000000..f2383b1
--- /dev/null
+++ b/ci/mvn-release.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+CURRENT_VERSION=`xmllint --xpath '/*[local-name()="project"]/*[local-name()="version"]/text()' pom.xml`
+
+if [[ $CURRENT_VERSION == *-SNAPSHOT ]]; then
+ NEW_VERSION=${CURRENT_VERSION%'-SNAPSHOT'}
+ NEXT_VERSION=`bash ci/semver.sh -p $NEW_VERSION`
+ NEXT_SNAPSHOT="$NEXT_VERSION-SNAPSHOT"
+ echo "perform release of $NEW_VERSION from $CURRENT_VERSION and set next develop version $NEXT_SNAPSHOT"
+
+ mvn versions:set -DnewVersion=$NEW_VERSION versions:commit --no-transfer-progress
+
+ echo "commit new release version"
+ git commit -a -m "Release $NEW_VERSION: set main to new release version"
+
+ echo "Update version in README.md"
+ sed -i -e "s|[0-9A-Za-z._-]\{1,\}|$NEW_VERSION|g" README.md && rm -f README.md-e
+ git commit -a -m "Release $NEW_VERSION: Update README.md"
+
+ echo "create tag for new release"
+ git tag -a $NEW_VERSION -m "Release $NEW_VERSION: tag release"
+
+ echo "merge main back to develop"
+ git fetch --all
+ git checkout develop
+ git merge main
+
+ mvn versions:set -DnewVersion=$NEXT_SNAPSHOT versions:commit --no-transfer-progress
+
+ echo "commit new snapshot version"
+ git commit -a -m "Release $NEW_VERSION: set develop to next development version $NEXT_SNAPSHOT"
+
+ git push --all
+ git push --tags
+fi
diff --git a/ci/semver.sh b/ci/semver.sh
new file mode 100644
index 0000000..b731ce9
--- /dev/null
+++ b/ci/semver.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+# Increment a version string using Semantic Versioning (SemVer) terminology.
+
+# Parse command line options.
+
+while getopts ":Mmp" Option
+do
+ case $Option in
+ M ) major=true;;
+ m ) minor=true;;
+ p ) patch=true;;
+ esac
+done
+
+shift $(($OPTIND - 1))
+
+version=$1
+
+# Build array from version string.
+
+a=( ${version//./ } )
+
+# If version string is missing or has the wrong number of members, show usage message.
+
+if [ ${#a[@]} -ne 3 ]
+then
+ echo "usage: $(basename $0) [-Mmp] major.minor.patch"
+ exit 1
+fi
+
+# Increment version numbers as requested.
+
+if [ ! -z $major ]
+then
+ ((a[0]++))
+ a[1]=0
+ a[2]=0
+fi
+
+if [ ! -z $minor ]
+then
+ ((a[1]++))
+ a[2]=0
+fi
+
+if [ ! -z $patch ]
+then
+ ((a[2]++))
+fi
+
+echo "${a[0]}.${a[1]}.${a[2]}"
diff --git a/ci/setup-git.sh b/ci/setup-git.sh
new file mode 100755
index 0000000..0a09a30
--- /dev/null
+++ b/ci/setup-git.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+git config --global user.email "oss@namics.com"
+git config --global user.name "Namics OSS CI"
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f2b31bb
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,245 @@
+
+
+ 4.0.0
+
+ com.merkle.oss.magnolia
+ magnolia-testing
+ 0.0.1-SNAPSHOT
+
+ ${project.artifactId}
+ Magnolia testing
+ https://github.com/merkle-open/magnolia-testing
+
+
+
+ MIT License
+ https://opensource.org/licenses/MIT
+ repo
+
+
+
+
+
+ Merkle Magnolia
+ magnolia@merkle.com
+ Merkle DACH
+ https://merkleinc.ch
+
+
+
+
+ https://github.com/merkle-open/magnolia-testing
+ scm:git:git@github.com:merkle-open/magnolia-testing.git
+ scm:git:git@github.com:merkle-open/magnolia-testing.git
+
+
+
+
+ 6.3.1
+ 3.0.2
+
+ 5.11.1
+ 5.14.0
+
+
+ 3.13.0
+ 3.3.0
+ 3.8.0
+ 3.2.5
+ 0.5.0
+ 3.5.0
+
+ 17
+ UTF-8
+
+
+
+
+
+
+ info.magnolia.bundle
+ magnolia-bundle-parent
+ ${magnolia.version}
+ pom
+ import
+
+
+ com.google.code.findbugs
+ jsr305
+ ${jsr305.nullable.version}
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.version}
+
+
+
+
+
+
+
+ info.magnolia
+ magnolia-core
+
+
+ info.magnolia
+ magnolia-rendering
+
+
+ info.magnolia
+ magnolia-templating
+
+
+ com.google.code.findbugs
+ jsr305
+
+
+ org.junit.jupiter
+ junit-jupiter
+
+
+ info.magnolia
+ magnolia-core
+ test-jar
+ compile
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${mvn.compiler.plugin.version}
+
+
+ ${javaVersion}
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${mvn.source.plugin.version}
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${mvn.javadoc.version}
+
+ false
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${mvn.surefire.plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ true
+
+
+
+
+
+
+
+
+ magnolia.public.group
+ https://nexus.magnolia-cms.com/content/groups/public
+
+ true
+
+
+
+ magnolia.enterprise.group
+ https://nexus.magnolia-cms.com/content/groups/enterprise
+
+ false
+
+
+
+
+
+ vaadin-addons
+ https://maven.vaadin.com/vaadin-addons
+
+
+
+
+
+
+
+ central
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+ deploy
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ ${mvn.gpg.plugin.version}
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ ${mvn.sonatype.publishing.plugin.version}
+ true
+
+ central
+ true
+ published
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/MagnoliaGuiceContextTestExtension.java b/src/main/java/com/merkle/oss/magnolia/testing/MagnoliaGuiceContextTestExtension.java
new file mode 100644
index 0000000..618b8cb
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/MagnoliaGuiceContextTestExtension.java
@@ -0,0 +1,23 @@
+package com.merkle.oss.magnolia.testing;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import com.merkle.oss.magnolia.testing.configuration.MagnoliaIntegrationTestInitializer;
+import com.merkle.oss.magnolia.testing.repository.RepositoryUtil;
+
+public class MagnoliaGuiceContextTestExtension implements BeforeEachCallback, AfterEachCallback {
+ private final MagnoliaIntegrationTestInitializer magnoliaIntegrationTestInitializer = new MagnoliaIntegrationTestInitializer();
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ magnoliaIntegrationTestInitializer.init(context);
+ new RepositoryUtil().load(context.getRequiredTestMethod());
+ }
+
+ @Override
+ public void afterEach(final ExtensionContext context) {
+ magnoliaIntegrationTestInitializer.destroy();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/MagnoliaIntegrationTestExtension.java b/src/main/java/com/merkle/oss/magnolia/testing/MagnoliaIntegrationTestExtension.java
new file mode 100644
index 0000000..c12f5ee
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/MagnoliaIntegrationTestExtension.java
@@ -0,0 +1,25 @@
+package com.merkle.oss.magnolia.testing;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import com.merkle.oss.magnolia.testing.configuration.MagnoliaIntegrationTestInitializer;
+import com.merkle.oss.magnolia.testing.repository.RepositoryUtil;
+
+public class MagnoliaIntegrationTestExtension implements BeforeEachCallback, AfterEachCallback {
+ private final MagnoliaIntegrationTestInitializer magnoliaIntegrationTestInitializer = new MagnoliaIntegrationTestInitializer();
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ magnoliaIntegrationTestInitializer.init(context);
+ magnoliaIntegrationTestInitializer.start();
+ new RepositoryUtil().load(context.getRequiredTestMethod());
+ }
+
+ @Override
+ public void afterEach(final ExtensionContext context) {
+ magnoliaIntegrationTestInitializer.stop();
+ magnoliaIntegrationTestInitializer.destroy();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/configuration/MagnoliaIntegrationTestInitializer.java b/src/main/java/com/merkle/oss/magnolia/testing/configuration/MagnoliaIntegrationTestInitializer.java
new file mode 100644
index 0000000..3d02190
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/configuration/MagnoliaIntegrationTestInitializer.java
@@ -0,0 +1,191 @@
+package com.merkle.oss.magnolia.testing.configuration;
+
+import info.magnolia.cms.security.SecuritySupport;
+import info.magnolia.cms.security.User;
+import info.magnolia.context.AbstractSystemContext;
+import info.magnolia.context.DefaultRepositoryStrategy;
+import info.magnolia.context.MgnlContext;
+import info.magnolia.context.SystemContext;
+import info.magnolia.context.WebContext;
+import info.magnolia.init.MagnoliaConfigurationProperties;
+import info.magnolia.init.MagnoliaServletContextListener;
+import info.magnolia.module.ModuleManagementException;
+import info.magnolia.module.ModuleManager;
+import info.magnolia.module.ModuleRegistry;
+import info.magnolia.objectfactory.Components;
+import info.magnolia.objectfactory.configuration.ComponentProviderConfiguration;
+import info.magnolia.objectfactory.configuration.ComponentProviderConfigurationBuilder;
+import info.magnolia.objectfactory.configuration.ImplementationConfiguration;
+import info.magnolia.objectfactory.guice.GuiceComponentProvider;
+import info.magnolia.objectfactory.guice.GuiceComponentProviderBuilder;
+import info.magnolia.repository.RepositoryManager;
+import info.magnolia.test.TestMagnoliaConfigurationProperties;
+import info.magnolia.test.mock.MockWebContext;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.lang3.time.StopWatch;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import com.google.inject.CreationException;
+import com.google.inject.Stage;
+import com.machinezoo.noexception.Exceptions;
+import com.merkle.oss.magnolia.testing.properties.IntegrationTestMagnoliaConfigurationProperties;
+import com.merkle.oss.magnolia.testing.servlet.MockServletContext;
+
+public class MagnoliaIntegrationTestInitializer {
+
+ public void init(final ExtensionContext extensionContext) throws Exception {
+ final StopWatch watch = new StopWatch();
+ watch.start();
+ try {
+ final Path appRootDir = Exceptions.wrap().get(() -> Files.createTempDirectory("magnolia-test"));
+ final GuiceComponentProvider platformComponentProvider = getPlatformComponentProvider(appRootDir, extensionContext);
+ final RepositoryManager repositoryManager = platformComponentProvider.getComponent(RepositoryManager.class);
+ repositoryManager.init();
+ final ModuleManager moduleManager = platformComponentProvider.getComponent(ModuleManager.class);
+ moduleManager.loadDefinitions();
+ MgnlContext.setInstance(Components.getComponent(SystemContext.class));
+
+ final IntegrationTestMagnoliaConfigurationProperties properties = platformComponentProvider.newInstance(IntegrationTestMagnoliaConfigurationProperties.class, appRootDir);
+ properties.init();
+ final GuiceComponentProvider systemComponentProvider = getSystemComponentProvider(extensionContext, platformComponentProvider, properties);
+ getMainComponentProvider(extensionContext, systemComponentProvider, properties);
+ } catch (CreationException e) {
+ throw new RuntimeException("Failed to init: " + e.getErrorMessages(), e);
+ }
+ watch.stop();
+ System.out.println("Initialization took "+watch.getDuration().toMillis()+"ms");
+ }
+
+ public void destroy() {
+ ((AbstractSystemContext)Components.getComponent(SystemContext.class)).getRepositoryStrategy().release();
+ MgnlContext.setInstance(null);
+ }
+
+ public void start() throws ModuleManagementException {
+ final StopWatch watch = new StopWatch();
+ watch.start();
+ final ModuleManager moduleManager = Components.getComponent(ModuleManager.class);
+ moduleManager.checkForInstallOrUpdates();
+ moduleManager.performInstallOrUpdate();
+ moduleManager.startModules();
+ MgnlContext.setInstance(getUserContext());
+ watch.stop();
+ System.out.println("Start took "+watch.getDuration().toMillis()+"ms");
+ }
+
+ public void stop() {
+ final StopWatch watch = new StopWatch();
+ watch.start();
+ final ModuleManager moduleManager = Components.getComponent(ModuleManager.class);
+ moduleManager.stopModules();
+ ((AbstractSystemContext)Components.getComponent(SystemContext.class)).getRepositoryStrategy().release();
+ MgnlContext.setInstance(null);
+ watch.stop();
+ System.out.println("Stop took "+watch.getDuration().toMillis()+"ms");
+ }
+
+ private WebContext getUserContext() {
+ final RepositoryManager repositoryManager = Components.getComponent(RepositoryManager.class);
+ final MockWebContext webContext = new MockWebContext();
+ final User user = Components.getComponent(SecuritySupport.class).getUserManager().getUser("superuser");
+ webContext.setUser(user);
+ webContext.setRepositoryStrategy(new DefaultRepositoryStrategy(repositoryManager, webContext));
+ return webContext;
+ }
+
+ private GuiceComponentProvider getPlatformComponentProvider(final Path appRootDir, final ExtensionContext extensionContext) throws IOException {
+ final TestMagnoliaConfigurationProperties properties = new TestMagnoliaConfigurationProperties(IntegrationTestMagnoliaConfigurationProperties.getInitialPropertySources(appRootDir, extensionContext));
+ final ComponentProviderConfiguration config = new ComponentProviderConfigurationBuilder().readConfiguration(List.of(
+ MagnoliaServletContextListener.DEFAULT_PLATFORM_COMPONENTS_CONFIG_LOCATION,
+ "/configuration/platform-components.xml"
+ ), "platform");
+ config.registerInstance(ServletContext.class, new MockServletContext());
+ applyAnnotationComponents(extensionContext, TestConfiguration.Component.Provider.PLATFORM, config);
+ config.registerInstance(MagnoliaConfigurationProperties.class, properties);
+ config.registerInstance(ExtensionContext.class, extensionContext);
+ final GuiceComponentProviderBuilder builder = new GuiceComponentProviderBuilder();
+ builder.withConfiguration(config);
+ builder.inStage(Stage.PRODUCTION);
+ builder.exposeGlobally();
+ return builder.build();
+ }
+
+ private GuiceComponentProvider getSystemComponentProvider(final ExtensionContext extensionContext, final GuiceComponentProvider parent, final MagnoliaConfigurationProperties properties) {
+ final ComponentProviderConfiguration config = merge(
+ new ComponentProviderConfigurationBuilder().readConfiguration(List.of("/configuration/system-components.xml"), "system"),
+ new ComponentProviderConfigurationBuilder().getComponentsFromModules(
+ "system",
+ parent.getComponent(ModuleRegistry.class).getModuleDefinitions()
+ )
+ );
+ applyAnnotationComponents(extensionContext, TestConfiguration.Component.Provider.SYSTEM, config);
+ config.registerInstance(MagnoliaConfigurationProperties.class, properties);
+ final GuiceComponentProviderBuilder builder = new GuiceComponentProviderBuilder();
+ builder.withConfiguration(config);
+ builder.withParent(parent);
+ builder.exposeGlobally();
+ return builder.build();
+ }
+
+ private GuiceComponentProvider getMainComponentProvider(final ExtensionContext extensionContext, final GuiceComponentProvider parent, final MagnoliaConfigurationProperties properties) {
+ final ComponentProviderConfiguration config = new ComponentProviderConfigurationBuilder().getComponentsFromModules(
+ "main",
+ parent.getComponent(ModuleRegistry.class).getModuleDefinitions()
+ );
+ applyAnnotationComponents(extensionContext, TestConfiguration.Component.Provider.MAIN, config);
+ config.registerInstance(MagnoliaConfigurationProperties.class, properties);
+ GuiceComponentProviderBuilder builder = new GuiceComponentProviderBuilder();
+ builder.withConfiguration(config);
+ builder.withParent((GuiceComponentProvider) Components.getComponentProvider());
+ builder.exposeGlobally();
+ return builder.build();
+ }
+
+ private ComponentProviderConfiguration merge(final ComponentProviderConfiguration config1, final ComponentProviderConfiguration config2) {
+ final ComponentProviderConfiguration merged = new ComponentProviderConfiguration();
+ Stream
+ .of(config1, config2)
+ .flatMap(config -> config.getAnnotatedComponents().entrySet().stream())
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1))
+ .values()
+ .forEach(merged::addComponent);
+ Stream
+ .of(config1, config2)
+ .flatMap(config -> config.getAnnotatedTypeMappings().entrySet().stream())
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1))
+ .forEach(merged::addTypeMapping);
+ return merged;
+ }
+
+ private void applyAnnotationComponents(final ExtensionContext extensionContext, final TestConfiguration.Component.Provider provider, final ComponentProviderConfiguration config) {
+ getComponents(extensionContext)
+ .filter(component -> provider.equals(component.provider()))
+ .map(component -> {
+ final ImplementationConfiguration configuration = new ImplementationConfiguration<>();
+ configuration.setType(component.type());
+ configuration.setImplementation(component.implementation());
+ configuration.setScope(TestConfiguration.Component.Scope.SINGLETON.equals(component.scope()) ? "singleton" : null);
+ configuration.setLazy(component.lazy());
+ return configuration;
+ })
+ .forEach(config::addComponent);
+ }
+
+ private Stream getComponents(final ExtensionContext extensionContext) {
+ return Optional.ofNullable(extensionContext.getRequiredTestMethod()).map(method -> method.getAnnotation(TestConfiguration.class)).or(() ->
+ Optional.ofNullable(extensionContext.getRequiredTestClass()).map(clazz -> clazz.getAnnotation(TestConfiguration.class))
+ ).stream().map(TestConfiguration::components).flatMap(Arrays::stream);
+ }
+}
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/configuration/TestConfiguration.java b/src/main/java/com/merkle/oss/magnolia/testing/configuration/TestConfiguration.java
new file mode 100644
index 0000000..c76b9eb
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/configuration/TestConfiguration.java
@@ -0,0 +1,34 @@
+package com.merkle.oss.magnolia.testing.configuration;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Inherited
+public @interface TestConfiguration {
+ String[] magnoliaProperties() default {};
+ Component[] components() default {};
+
+ @interface Component {
+ Provider provider() default Provider.MAIN;
+ Class> type();
+ Class> implementation();
+ Scope scope() default Scope.DEFAULT;
+ boolean lazy() default false;
+
+ enum Provider {
+ MAIN,
+ SYSTEM,
+ PLATFORM
+ }
+
+ enum Scope {
+ SINGLETON,
+ DEFAULT
+ }
+ }
+}
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/module/TestCoreModuleVersionHandler.java b/src/main/java/com/merkle/oss/magnolia/testing/module/TestCoreModuleVersionHandler.java
new file mode 100644
index 0000000..09e60d9
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/module/TestCoreModuleVersionHandler.java
@@ -0,0 +1,25 @@
+package com.merkle.oss.magnolia.testing.module;
+
+import info.magnolia.jcr.util.NodeNameHelper;
+import info.magnolia.module.delta.Condition;
+import info.magnolia.repository.RepositoryManager;
+import info.magnolia.setup.CoreModuleVersionHandler;
+
+import java.util.List;
+
+import com.google.inject.Inject;
+
+public class TestCoreModuleVersionHandler extends CoreModuleVersionHandler {
+ @Inject
+ public TestCoreModuleVersionHandler(
+ final RepositoryManager repositoryManager,
+ final NodeNameHelper nodeNameHelper
+ ) {
+ super(repositoryManager, nodeNameHelper);
+ }
+
+ @Override
+ protected List getInstallConditions() {
+ return List.of();
+ }
+}
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/module/TestModuleManager.java b/src/main/java/com/merkle/oss/magnolia/testing/module/TestModuleManager.java
new file mode 100644
index 0000000..b9ed3c0
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/module/TestModuleManager.java
@@ -0,0 +1,43 @@
+package com.merkle.oss.magnolia.testing.module;
+
+import info.magnolia.health.HealthCheckRegistry;
+import info.magnolia.jcr.node2bean.Node2BeanProcessor;
+import info.magnolia.module.InstallContextImpl;
+import info.magnolia.module.ModuleLifecycleContextImpl;
+import info.magnolia.module.ModuleManagerImpl;
+import info.magnolia.module.ModuleRegistry;
+import info.magnolia.module.model.ModuleDefinition;
+import info.magnolia.module.model.reader.DependencyChecker;
+import info.magnolia.module.model.reader.ModuleDefinitionReader;
+import info.magnolia.repository.RepositoryManager;
+
+import java.util.Set;
+
+import org.apache.commons.lang3.time.StopWatch;
+
+import com.google.inject.Inject;
+
+public class TestModuleManager extends ModuleManagerImpl {
+
+ @Inject
+ public TestModuleManager(
+ final InstallContextImpl installContext,
+ final Set moduleDefinitionReaders,
+ final ModuleRegistry moduleRegistry,
+ final DependencyChecker dependencyChecker,
+ final Node2BeanProcessor nodeToBean,
+ final RepositoryManager repositoryManager,
+ final HealthCheckRegistry healthCheckRegistry
+ ) {
+ super(installContext, moduleDefinitionReaders, moduleRegistry, dependencyChecker, nodeToBean, repositoryManager, healthCheckRegistry);
+ }
+
+ @Override
+ protected void startModule(Object moduleInstance, final ModuleDefinition moduleDefinition, final ModuleLifecycleContextImpl lifecycleContext) {
+ final StopWatch watch = new StopWatch();
+ watch.start();
+ super.startModule(moduleInstance, moduleDefinition, lifecycleContext);
+ watch.stop();
+ System.out.println("Starting module " + moduleDefinition.getName() + " took " + watch.getDuration().toMillis() + "ms");
+ }
+}
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/properties/IntegrationTestMagnoliaConfigurationProperties.java b/src/main/java/com/merkle/oss/magnolia/testing/properties/IntegrationTestMagnoliaConfigurationProperties.java
new file mode 100644
index 0000000..50cae7d
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/properties/IntegrationTestMagnoliaConfigurationProperties.java
@@ -0,0 +1,75 @@
+package com.merkle.oss.magnolia.testing.properties;
+
+import info.magnolia.init.DefaultMagnoliaConfigurationProperties;
+import info.magnolia.init.MagnoliaInitPaths;
+import info.magnolia.init.MagnoliaPropertiesResolver;
+import info.magnolia.init.PropertySource;
+import info.magnolia.init.properties.EnvironmentPropertySource;
+import info.magnolia.init.properties.SystemPropertySource;
+import info.magnolia.module.ModuleRegistry;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import com.google.inject.Inject;
+import com.machinezoo.noexception.Exceptions;
+import com.merkle.oss.magnolia.testing.configuration.TestConfiguration;
+
+public class IntegrationTestMagnoliaConfigurationProperties extends DefaultMagnoliaConfigurationProperties {
+ private final Path appRootDir;
+ private final ExtensionContext extensionContext;
+
+ @Inject
+ public IntegrationTestMagnoliaConfigurationProperties(
+ final MagnoliaInitPaths initPaths,
+ final ModuleRegistry moduleRegistry,
+ final MagnoliaPropertiesResolver resolver,
+ final SystemPropertySource systemPropertySource,
+ final EnvironmentPropertySource environmentPropertySource,
+ final Path appRootDir,
+ final ExtensionContext extensionContext
+ ) {
+ super(initPaths, moduleRegistry, resolver, systemPropertySource, environmentPropertySource);
+ this.appRootDir = appRootDir;
+ this.extensionContext = extensionContext;
+ }
+
+ @Override
+ public void init() throws Exception {
+ sources.addAll(getInitialPropertySources(appRootDir, extensionContext));
+ super.init();
+ }
+
+ @Override
+ protected String parseStringValue(final String strVal, final Set visitedPlaceholders) {
+ return super.parseStringValue(strVal, visitedPlaceholders);
+ }
+
+ public static List getInitialPropertySources(final Path appRootDir, final ExtensionContext extensionContext) throws IOException {
+ return Stream.concat(
+ getCustomProperties(extensionContext)
+ .filter(path -> IntegrationTestMagnoliaConfigurationProperties.class.getResource(path) != null)
+ .map(path -> Exceptions.wrap().get(() -> new ReferencingClasspathPropertySource(path, appRootDir))),
+ Stream.of(
+ new TestPropertySource(appRootDir),
+ new ReferencingClasspathPropertySource("/default-test-magnolia.properties", appRootDir)
+ )
+ ).toList();
+ }
+
+ private static Stream getCustomProperties(final ExtensionContext extensionContext) {
+ return Stream
+ .of(extensionContext.getRequiredTestMethod(), extensionContext.getRequiredTestClass())
+ .map(method -> Optional.ofNullable(method.getAnnotation(TestConfiguration.class)))
+ .flatMap(Optional::stream)
+ .map(TestConfiguration::magnoliaProperties)
+ .flatMap(Arrays::stream);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/properties/ReferencingClasspathPropertySource.java b/src/main/java/com/merkle/oss/magnolia/testing/properties/ReferencingClasspathPropertySource.java
new file mode 100644
index 0000000..a0abfe6
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/properties/ReferencingClasspathPropertySource.java
@@ -0,0 +1,39 @@
+package com.merkle.oss.magnolia.testing.properties;
+
+import info.magnolia.init.properties.ClasspathPropertySource;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+
+public class ReferencingClasspathPropertySource extends ClasspathPropertySource {
+ private final Path appRootDir;
+
+ public ReferencingClasspathPropertySource(final String path, final Path appRootDir) throws IOException {
+ super(path);
+ this.appRootDir = appRootDir;
+ }
+
+ @Override
+ public String getProperty(final String key) {
+ final String value = super.getProperty(key);
+ if(value != null && value.startsWith("classpath:")) {
+ try {
+ final String classpathResource = StringUtils.removeStart(value, "classpath:");
+ final String extension = FilenameUtils.getExtension(classpathResource);
+ final String baseName = FilenameUtils.getBaseName(classpathResource);
+ final File tempFile = Files.createTempFile(appRootDir, baseName, "."+extension).toFile();
+ FileUtils.copyURLToFile(getClass().getResource(classpathResource), tempFile);
+ return tempFile.getAbsolutePath();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return value;
+ }
+}
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/properties/TestPropertySource.java b/src/main/java/com/merkle/oss/magnolia/testing/properties/TestPropertySource.java
new file mode 100644
index 0000000..2257535
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/properties/TestPropertySource.java
@@ -0,0 +1,43 @@
+package com.merkle.oss.magnolia.testing.properties;
+
+import info.magnolia.init.PropertySource;
+
+import java.nio.file.Path;
+import java.util.Set;
+
+public class TestPropertySource implements PropertySource {
+ private final Path appRootDir;
+
+ public TestPropertySource(final Path appRootDir) {
+ this.appRootDir = appRootDir;
+ }
+
+ @Override
+ public Set getKeys() {
+ return Set.of("magnolia.app.rootdir", "resource.home");
+ }
+
+ @Override
+ public String getProperty(final String key) {
+ return switch (key) {
+ case "magnolia.app.rootdir" -> appRootDir.toFile().getAbsolutePath();
+ case "resource.home" -> getClass().getClassLoader().getResource("").getFile();
+ default -> null;
+ };
+ }
+
+ @Override
+ public boolean getBooleanProperty(final String key) {
+ return false;
+ }
+
+ @Override
+ public boolean hasProperty(final String key) {
+ return getKeys().contains(key);
+ }
+
+ @Override
+ public String describe() {
+ return "magnolia integration test properties";
+ }
+}
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/repository/Repository.java b/src/main/java/com/merkle/oss/magnolia/testing/repository/Repository.java
new file mode 100644
index 0000000..6bddf0f
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/repository/Repository.java
@@ -0,0 +1,27 @@
+package com.merkle.oss.magnolia.testing.repository;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Inherited
+public @interface Repository {
+ Workspace[] workspaces();
+ NodeTypesDefinition[] nodeTypes() default {};
+
+ @interface Workspace {
+ String name();
+ String xml();
+ String repositoryId() default "magnolia";
+ boolean create() default false;
+ }
+
+ @interface NodeTypesDefinition {
+ String repositoryId() default "magnolia";
+ String cnd();
+ }
+}
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/repository/RepositoryUtil.java b/src/main/java/com/merkle/oss/magnolia/testing/repository/RepositoryUtil.java
new file mode 100644
index 0000000..4b64c5c
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/repository/RepositoryUtil.java
@@ -0,0 +1,98 @@
+package com.merkle.oss.magnolia.testing.repository;
+
+import info.magnolia.objectfactory.Components;
+import info.magnolia.repository.RepositoryManager;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+public class RepositoryUtil {
+ private final RepositoryManager repositoryManager;
+
+ public RepositoryUtil() {
+ this(Components.getComponent(RepositoryManager.class));
+ }
+
+ public RepositoryUtil(final RepositoryManager repositoryManager) {
+ this.repositoryManager = repositoryManager;
+ }
+
+ public void load(final Method testMethod) throws IOException, RepositoryException {
+ final Class> testClass = testMethod.getDeclaringClass();
+ final Repository repository = Optional
+ .ofNullable(testMethod.getAnnotation(Repository.class))
+ .or(() -> Optional.ofNullable(testClass.getAnnotation(Repository.class)))
+ .orElse(null);
+ if(repository != null) {
+ load(testClass, repository);
+ }
+ }
+
+ private void load(final Class> testClass, final Repository repository) throws IOException, RepositoryException {
+ for (Repository.NodeTypesDefinition nodeTypesDefinition : repository.nodeTypes()) {
+ repositoryManager.getRepositoryProvider(nodeTypesDefinition.repositoryId()).registerNodeTypes(nodeTypesDefinition.cnd());
+ }
+ for (Repository.Workspace workspace : repository.workspaces()) {
+ if (!repositoryManager.hasWorkspace(workspace.name()) && workspace.create()) {
+ repositoryManager.loadWorkspace(workspace.repositoryId(), workspace.name());
+ }
+ final Session session = repositoryManager.getSystemSession(workspace.name());
+ session.importXML("/", testClass.getResourceAsStream(workspace.xml()), ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+ session.save();
+ session.logout();
+ }
+ }
+
+ public void dump(final String workspace) throws RepositoryException {
+ final Session session = repositoryManager.getSystemSession(workspace);
+ dump(session.getRootNode());
+ session.logout();
+ }
+
+ /**
+ * Recursively outputs the contents of the given node.
+ */
+ public void dump(final Node node) throws RepositoryException {
+ // First output the node path
+ System.out.println(node.getPath());
+ // Skip the virtual (and large!) jcr:system subtree
+ if (node.getName().equals("jcr:system")) {
+ return;
+ }
+
+ // Then output the properties
+ PropertyIterator properties = node.getProperties();
+ while (properties.hasNext()) {
+ Property property = properties.nextProperty();
+ if (property.getDefinition().isMultiple()) {
+ // A multi-valued property, print all values
+ Value[] values = property.getValues();
+ for (int i = 0; i < values.length; i++) {
+ System.out.println(property.getPath() + " = " + values[i].getString());
+ }
+ } else {
+ // A single-valued property
+ System.out.println(property.getPath() + " = " + property.getString());
+ }
+ }
+
+ // Finally output all the child nodes recursively
+ NodeIterator nodes = node.getNodes();
+ while (nodes.hasNext()) {
+ dump(nodes.nextNode());
+ }
+ }
+}
diff --git a/src/main/java/com/merkle/oss/magnolia/testing/servlet/MockServletContext.java b/src/main/java/com/merkle/oss/magnolia/testing/servlet/MockServletContext.java
new file mode 100644
index 0000000..417afe0
--- /dev/null
+++ b/src/main/java/com/merkle/oss/magnolia/testing/servlet/MockServletContext.java
@@ -0,0 +1,317 @@
+package com.merkle.oss.magnolia.testing.servlet;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspConfigDescriptor;
+
+public class MockServletContext implements ServletContext {
+ @Override
+ public String getContextPath() {
+ return "";
+ }
+
+ @Override
+ public ServletContext getContext(String uripath) {
+ return null;
+ }
+
+ @Override
+ public int getMajorVersion() {
+ return 0;
+ }
+
+ @Override
+ public int getMinorVersion() {
+ return 0;
+ }
+
+ @Override
+ public int getEffectiveMajorVersion() {
+ return 0;
+ }
+
+ @Override
+ public int getEffectiveMinorVersion() {
+ return 0;
+ }
+
+ @Override
+ public String getMimeType(String file) {
+ return "";
+ }
+
+ @Override
+ public Set getResourcePaths(String path) {
+ return Set.of();
+ }
+
+ @Override
+ public URL getResource(String path) throws MalformedURLException {
+ return null;
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String path) {
+ return null;
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(String path) {
+ return null;
+ }
+
+ @Override
+ public RequestDispatcher getNamedDispatcher(String name) {
+ return null;
+ }
+
+ @Override
+ public Servlet getServlet(String name) throws ServletException {
+ return null;
+ }
+
+ @Override
+ public Enumeration getServlets() {
+ return null;
+ }
+
+ @Override
+ public Enumeration getServletNames() {
+ return null;
+ }
+
+ @Override
+ public void log(String msg) {
+
+ }
+
+ @Override
+ public void log(Exception exception, String msg) {
+
+ }
+
+ @Override
+ public void log(String message, Throwable throwable) {
+
+ }
+
+ @Override
+ public String getRealPath(String path) {
+ return "";
+ }
+
+ @Override
+ public String getServerInfo() {
+ return "";
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ return "";
+ }
+
+ @Override
+ public Enumeration getInitParameterNames() {
+ return null;
+ }
+
+ @Override
+ public boolean setInitParameter(String name, String value) {
+ return false;
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return null;
+ }
+
+ @Override
+ public Enumeration getAttributeNames() {
+ return null;
+ }
+
+ @Override
+ public void setAttribute(String name, Object object) {
+
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+
+ }
+
+ @Override
+ public String getServletContextName() {
+ return "";
+ }
+
+ @Override
+ public ServletRegistration.Dynamic addServlet(String servletName, String className) {
+ return null;
+ }
+
+ @Override
+ public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) {
+ return null;
+ }
+
+ @Override
+ public ServletRegistration.Dynamic addServlet(String servletName, Class extends Servlet> servletClass) {
+ return null;
+ }
+
+ @Override
+ public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) {
+ return null;
+ }
+
+ @Override
+ public T createServlet(Class clazz) throws ServletException {
+ return null;
+ }
+
+ @Override
+ public ServletRegistration getServletRegistration(String servletName) {
+ return null;
+ }
+
+ @Override
+ public Map getServletRegistrations() {
+ return Map.of();
+ }
+
+ @Override
+ public FilterRegistration.Dynamic addFilter(String filterName, String className) {
+ return null;
+ }
+
+ @Override
+ public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) {
+ return null;
+ }
+
+ @Override
+ public FilterRegistration.Dynamic addFilter(String filterName, Class extends Filter> filterClass) {
+ return null;
+ }
+
+ @Override
+ public T createFilter(Class clazz) throws ServletException {
+ return null;
+ }
+
+ @Override
+ public FilterRegistration getFilterRegistration(String filterName) {
+ return null;
+ }
+
+ @Override
+ public Map getFilterRegistrations() {
+ return Map.of();
+ }
+
+ @Override
+ public SessionCookieConfig getSessionCookieConfig() {
+ return null;
+ }
+
+ @Override
+ public void setSessionTrackingModes(Set sessionTrackingModes) {
+
+ }
+
+ @Override
+ public Set getDefaultSessionTrackingModes() {
+ return Set.of();
+ }
+
+ @Override
+ public Set getEffectiveSessionTrackingModes() {
+ return Set.of();
+ }
+
+ @Override
+ public void addListener(String className) {
+
+ }
+
+ @Override
+ public void addListener(T t) {
+
+ }
+
+ @Override
+ public void addListener(Class extends EventListener> listenerClass) {
+
+ }
+
+ @Override
+ public T createListener(Class clazz) throws ServletException {
+ return null;
+ }
+
+ @Override
+ public JspConfigDescriptor getJspConfigDescriptor() {
+ return null;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return null;
+ }
+
+ @Override
+ public void declareRoles(String... roleNames) {
+
+ }
+
+ @Override
+ public String getVirtualServerName() {
+ return "";
+ }
+
+ @Override
+ public int getSessionTimeout() {
+ return 0;
+ }
+
+ @Override
+ public void setSessionTimeout(int sessionTimeout) {
+
+ }
+
+ @Override
+ public String getRequestCharacterEncoding() {
+ return "";
+ }
+
+ @Override
+ public void setRequestCharacterEncoding(String encoding) {
+
+ }
+
+ @Override
+ public String getResponseCharacterEncoding() {
+ return "";
+ }
+
+ @Override
+ public void setResponseCharacterEncoding(String encoding) {
+
+ }
+}
diff --git a/src/main/resources/configuration/platform-components.xml b/src/main/resources/configuration/platform-components.xml
new file mode 100644
index 0000000..9ccf545
--- /dev/null
+++ b/src/main/resources/configuration/platform-components.xml
@@ -0,0 +1,18 @@
+
+
+ platform
+
+ info.magnolia.context.SystemContext
+ info.magnolia.context.SingleJCRSessionSystemContext
+ singleton
+
+
+ info.magnolia.module.ModuleManager
+ com.merkle.oss.magnolia.testing.module.TestModuleManager
+ singleton
+
+
+ info.magnolia.setup.CoreModuleVersionHandler
+ com.merkle.oss.magnolia.testing.module.TestCoreModuleVersionHandler
+
+
diff --git a/src/main/resources/configuration/system-components.xml b/src/main/resources/configuration/system-components.xml
new file mode 100644
index 0000000..290d13e
--- /dev/null
+++ b/src/main/resources/configuration/system-components.xml
@@ -0,0 +1,9 @@
+
+
+ system
+
+ info.magnolia.context.SystemContext
+ info.magnolia.context.SingleJCRSessionSystemContext
+ singleton
+
+
diff --git a/src/main/resources/default-test-magnolia.properties b/src/main/resources/default-test-magnolia.properties
new file mode 100644
index 0000000..d1be3e4
--- /dev/null
+++ b/src/main/resources/default-test-magnolia.properties
@@ -0,0 +1,12 @@
+magnolia.home=${magnolia.app.rootdir}
+# The directory to expose file system resources from
+magnolia.resources.dir=${magnolia.home}/modules
+magnolia.repositories.home=${magnolia.home}/repositories
+magnolia.repositories.config=classpath:/repository/repositories.xml
+magnolia.repositories.jackrabbit.config=classpath:/repository/InMemoryJcrRepositoryConfiguration.xml
+magnolia.clusterid=test-01
+
+magnolia.password.manager.key.location=${magnolia.home}/password-manager-key
+magnolia.upload.tmpdir=${magnolia.home}/upload-tmp-dir
+magnolia.bootstrap.dir=WEB-INF/bootstrap/author WEB-INF/bootstrap/common
+
diff --git a/src/main/resources/repository/InMemoryJcrRepositoryConfiguration.xml b/src/main/resources/repository/InMemoryJcrRepositoryConfiguration.xml
new file mode 100644
index 0000000..47df98f
--- /dev/null
+++ b/src/main/resources/repository/InMemoryJcrRepositoryConfiguration.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/repository/repositories.xml b/src/main/resources/repository/repositories.xml
new file mode 100644
index 0000000..9f5669d
--- /dev/null
+++ b/src/main/resources/repository/repositories.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+ ]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/com/merkle/oss/magnolia/testing/SampleGuiceContextTest.java b/src/test/java/com/merkle/oss/magnolia/testing/SampleGuiceContextTest.java
new file mode 100644
index 0000000..d6a7cc6
--- /dev/null
+++ b/src/test/java/com/merkle/oss/magnolia/testing/SampleGuiceContextTest.java
@@ -0,0 +1,66 @@
+package com.merkle.oss.magnolia.testing;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import info.magnolia.context.MgnlContext;
+import info.magnolia.objectfactory.Components;
+import info.magnolia.repository.RepositoryConstants;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import com.merkle.oss.magnolia.testing.configuration.TestConfiguration;
+import com.merkle.oss.magnolia.testing.repository.Repository;
+
+@Repository(workspaces = {
+ @Repository.Workspace(name = RepositoryConstants.WEBSITE, xml = "jcr.xml")
+})
+@ExtendWith(MagnoliaGuiceContextTestExtension.class)
+class SampleGuiceContextTest {
+
+ @Test
+ void someMethod() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession(RepositoryConstants.WEBSITE);
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some value 42!", someInterface.someMethod(node));
+ }
+
+ @Test
+ @TestConfiguration(components = {
+ @TestConfiguration.Component(type = SomeInterface.class, implementation = SomeInterface.SomeOtherImplementation.class)
+ })
+ void someOtherMethod() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession(RepositoryConstants.WEBSITE);
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some other implementation!", someInterface.someMethod(node));
+ }
+
+ @Repository(
+ nodeTypes = {@Repository.NodeTypesDefinition(cnd = "/mgnl-nodetypes/testing-nodetypes.cnd")},
+ workspaces = {@Repository.Workspace(name = RepositoryConstants.WEBSITE, xml = "jcr-custom-nodetype.xml")}
+ )
+ @Test
+ void customNodeType() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession(RepositoryConstants.WEBSITE);
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some value 42!", someInterface.someMethod(node));
+ session.logout();
+ }
+
+ @Repository(workspaces = {@Repository.Workspace(name = "testing", xml = "jcr.xml", create = true)})
+ @Test
+ void customWorkspace() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession("testing");
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some value 42!", someInterface.someMethod(node));
+ session.logout();
+ }
+}
diff --git a/src/test/java/com/merkle/oss/magnolia/testing/SampleIntegrationTest.java b/src/test/java/com/merkle/oss/magnolia/testing/SampleIntegrationTest.java
new file mode 100644
index 0000000..5d6bcab
--- /dev/null
+++ b/src/test/java/com/merkle/oss/magnolia/testing/SampleIntegrationTest.java
@@ -0,0 +1,57 @@
+package com.merkle.oss.magnolia.testing;
+
+import static io.smallrye.common.constraint.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import info.magnolia.context.MgnlContext;
+import info.magnolia.objectfactory.Components;
+import info.magnolia.repository.RepositoryConstants;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import com.merkle.oss.magnolia.testing.repository.Repository;
+
+@ExtendWith(MagnoliaIntegrationTestExtension.class)
+class SampleIntegrationTest {
+
+ @Test
+ void moduleStart() {
+ final SomeModule someModule = Components.getComponent(SomeModule.class);
+ assertTrue(someModule.isStarted());
+ }
+
+ @Repository(workspaces = {@Repository.Workspace(name = RepositoryConstants.WEBSITE, xml = "jcr.xml")})
+ @Test
+ void someMethod() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession(RepositoryConstants.WEBSITE);
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some value 42!", someInterface.someMethod(node));
+ session.logout();
+ }
+
+ @Repository(workspaces = {@Repository.Workspace(name = RepositoryConstants.WEBSITE, xml = "jcr-custom-nodetype.xml")})
+ @Test
+ void customNodeType() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession(RepositoryConstants.WEBSITE);
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some value 42!", someInterface.someMethod(node));
+ session.logout();
+ }
+
+ @Repository(workspaces = {@Repository.Workspace(name = "testing", xml = "jcr.xml")})
+ @Test
+ void customWorkspace() throws RepositoryException {
+ final Session session = MgnlContext.getJCRSession("testing");
+ final Node node = session.getRootNode().getNode("0");
+ final SomeInterface someInterface = Components.getComponent(SomeInterface.class);
+ assertEquals("some value 42!", someInterface.someMethod(node));
+ session.logout();
+ }
+}
diff --git a/src/test/java/com/merkle/oss/magnolia/testing/SomeInterface.java b/src/test/java/com/merkle/oss/magnolia/testing/SomeInterface.java
new file mode 100644
index 0000000..6b0fcc8
--- /dev/null
+++ b/src/test/java/com/merkle/oss/magnolia/testing/SomeInterface.java
@@ -0,0 +1,23 @@
+package com.merkle.oss.magnolia.testing;
+
+import info.magnolia.jcr.util.PropertyUtil;
+
+import javax.jcr.Node;
+
+public interface SomeInterface {
+ String someMethod(Node node);
+
+ class SomeImplementation implements SomeInterface {
+ @Override
+ public String someMethod(final Node node) {
+ return PropertyUtil.getString(node, "someProperty");
+ }
+ }
+
+ class SomeOtherImplementation implements SomeInterface {
+ @Override
+ public String someMethod(final Node node) {
+ return "some other implementation!";
+ }
+ }
+}
diff --git a/src/test/java/com/merkle/oss/magnolia/testing/SomeModule.java b/src/test/java/com/merkle/oss/magnolia/testing/SomeModule.java
new file mode 100644
index 0000000..e122d03
--- /dev/null
+++ b/src/test/java/com/merkle/oss/magnolia/testing/SomeModule.java
@@ -0,0 +1,24 @@
+package com.merkle.oss.magnolia.testing;
+
+import info.magnolia.module.ModuleLifecycle;
+import info.magnolia.module.ModuleLifecycleContext;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class SomeModule implements ModuleLifecycle {
+ private final AtomicBoolean started = new AtomicBoolean();
+
+ @Override
+ public void start(final ModuleLifecycleContext moduleLifecycleContext) {
+ started.set(true);
+ }
+
+ @Override
+ public void stop(final ModuleLifecycleContext moduleLifecycleContext) {
+ started.set(false);
+ }
+
+ public boolean isStarted() {
+ return started.get();
+ }
+}
diff --git a/src/test/java/com/merkle/oss/magnolia/testing/configuration/TestingGuiceComponentConfigurer.java b/src/test/java/com/merkle/oss/magnolia/testing/configuration/TestingGuiceComponentConfigurer.java
new file mode 100644
index 0000000..573aa1a
--- /dev/null
+++ b/src/test/java/com/merkle/oss/magnolia/testing/configuration/TestingGuiceComponentConfigurer.java
@@ -0,0 +1,14 @@
+package com.merkle.oss.magnolia.testing.configuration;
+
+import info.magnolia.objectfactory.guice.AbstractGuiceComponentConfigurer;
+
+import java.time.ZoneId;
+
+public class TestingGuiceComponentConfigurer extends AbstractGuiceComponentConfigurer {
+
+ @Override
+ protected void configure() {
+ super.configure();
+ binder().bind(ZoneId.class).toProvider(() -> ZoneId.of("Europe/Zurich"));
+ }
+}
diff --git a/src/test/resources/META-INF/magnolia/magnolia-testing.xml b/src/test/resources/META-INF/magnolia/magnolia-testing.xml
new file mode 100644
index 0000000..3737d14
--- /dev/null
+++ b/src/test/resources/META-INF/magnolia/magnolia-testing.xml
@@ -0,0 +1,37 @@
+
+
+
+ magnolia-testing
+ Magnolia testing
+ magnolia testing oss repo
+ com.merkle.oss.magnolia.testing.SomeModule
+ 0.0.1
+
+
+ main
+
+ com.merkle.oss.magnolia.testing.configuration.TestingGuiceComponentConfigurer
+
+
+ com.merkle.oss.magnolia.testing.SomeInterface
+ com.merkle.oss.magnolia.testing.SomeInterface$SomeImplementation
+
+
+
+
+
+ core
+ */*
+
+
+
+
+
+ magnolia
+
+ testing
+
+ /mgnl-nodetypes/testing-nodetypes.cnd
+
+
+
diff --git a/src/test/resources/com/merkle/oss/magnolia/testing/jcr-custom-nodetype.xml b/src/test/resources/com/merkle/oss/magnolia/testing/jcr-custom-nodetype.xml
new file mode 100644
index 0000000..ab1336a
--- /dev/null
+++ b/src/test/resources/com/merkle/oss/magnolia/testing/jcr-custom-nodetype.xml
@@ -0,0 +1,33 @@
+
+
+
+ testing:test
+
+
+ af5be8b3-405f-4081-8f1d-76b808add588
+
+
+ dam
+
+
+ jcr:f3747f77-6387-47f3-a2b7-2329703e40e0
+
+
+ 2024-11-11T10:23:02.753+01:00
+
+
+ superuser
+
+
+ 2024-11-11T10:23:02.753+01:00
+
+
+ superuser
+
+
+ magnolia-testing:components/Test
+
+
+ some value 42!
+
+
diff --git a/src/test/resources/com/merkle/oss/magnolia/testing/jcr.xml b/src/test/resources/com/merkle/oss/magnolia/testing/jcr.xml
new file mode 100644
index 0000000..4dbfa72
--- /dev/null
+++ b/src/test/resources/com/merkle/oss/magnolia/testing/jcr.xml
@@ -0,0 +1,33 @@
+
+
+
+ mgnl:component
+
+
+ af5be8b3-405f-4081-8f1d-76b808add588
+
+
+ dam
+
+
+ jcr:f3747f77-6387-47f3-a2b7-2329703e40e0
+
+
+ 2024-11-11T10:23:02.753+01:00
+
+
+ superuser
+
+
+ 2024-11-11T10:23:02.753+01:00
+
+
+ superuser
+
+
+ magnolia-testing:components/Test
+
+
+ some value 42!
+
+
diff --git a/src/test/resources/mgnl-nodetypes/testing-nodetypes.cnd b/src/test/resources/mgnl-nodetypes/testing-nodetypes.cnd
new file mode 100644
index 0000000..2fc0773
--- /dev/null
+++ b/src/test/resources/mgnl-nodetypes/testing-nodetypes.cnd
@@ -0,0 +1,5 @@
+<'mgnl'='http://www.magnolia-cms.com/jcr/mgnl'>
+<'testing'='https://www.zg.ch/jcr/1.0/zug'>
+
+[testing:test] > mgnl:content
+ orderable
\ No newline at end of file
diff --git a/src/test/resources/mgnl-resources/test/test.txt b/src/test/resources/mgnl-resources/test/test.txt
new file mode 100644
index 0000000..c6fa431
--- /dev/null
+++ b/src/test/resources/mgnl-resources/test/test.txt
@@ -0,0 +1 @@
+somehow magnolia requires at least one mgnl-resource otherwise info.magnolia.resourceloader.classpath.service.impl.base.ClasspathServiceBase logs errors (test still runs fine though)
\ No newline at end of file