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} + ${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 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 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 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