You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2022/04/28 21:24:56 UTC

[isis] branch master updated: ISIS-3031: adds NoPermissionChecks and UserMementoRefiners for integ testing

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new ebc6d9b1e0 ISIS-3031: adds NoPermissionChecks and UserMementoRefiners for integ testing
ebc6d9b1e0 is described below

commit ebc6d9b1e085a84a07ef3f2f8cc11bd32cec523d
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Apr 28 22:24:40 2022 +0100

    ISIS-3031: adds NoPermissionChecks and UserMementoRefiners for integ testing
    
    plus docs
---
 .../adoc/modules/integtestsupport/pages/about.adoc | 48 -----------
 .../integtestsupport/pages/hints-and-tips.adoc     | 11 +++
 .../pages/influencing-the-interaction.adoc         | 94 ++++++++++++++++++++++
 .../integtestsupport/partials/module-nav.adoc      |  3 +
 .../applib/NoPermissionChecks.java                 | 40 +++++++++
 .../applib/UserMementoRefiners.java                | 44 ++++++++++
 6 files changed, 192 insertions(+), 48 deletions(-)

diff --git a/testing/integtestsupport/adoc/modules/integtestsupport/pages/about.adoc b/testing/integtestsupport/adoc/modules/integtestsupport/pages/about.adoc
index 18553c0603..99aeb9e952 100644
--- a/testing/integtestsupport/adoc/modules/integtestsupport/pages/about.adoc
+++ b/testing/integtestsupport/adoc/modules/integtestsupport/pages/about.adoc
@@ -626,55 +626,7 @@ Thus, the setup fixture runs the setup for the "inner-most" "leaf-level" modules
 
 
 
-== Validate MetaModel
 
-Metamodel validation checks for a number of semantic errors with the domain model.
-For example, if a supporting method (such as `default0UpdateName()` in `SimpleObject`) is misspelt, then this would be flagged.
-
-Running up the application will flag any metamodel validation issues, as will running an integration test.
-However, depending upon configuration, the metamodel may only be built lazily, meaning that issues will only be detected while the application is running, rather than at bootstrap.
-
-The `DomainModelValidator` is a simple utility class that will fullu rebuild the metamodel if required, and then verify that there are no issues.
-
-[source,java]
-.ValidateDomainModel_IntegTest.java
-----
-class ValidateDomainModel_IntegTest
-        extends ApplicationIntegTestAbstract {
-
-    @Inject ServiceRegistry serviceRegistry;
-
-    @Test
-    void validate() {
-        new DomainModelValidator(serviceRegistry).assertValid();
-    }
-}
-----
-
-To see this in action in the xref:docs:starters:simpleapp.adoc[simpleapp] starter app:
-
-* change `isis.applib.annotation.action.explicit` to `false` in `application.yml`
-* introduce an error, for example by renaming `default0UpdateName` to `default0UpdateFoo`.
-
-
-== Swagger Exporter
-
-@SpringBootTest(
-classes = {
-ApplicationIntegTestAbstract.AppManifest.class,
-IsisModuleViewerRestfulObjectsJaxrsResteasy4.class
-}
-)
-class SwaggerExport_IntegTest extends ApplicationIntegTestAbstract {
-
-    @Inject ServiceRegistry serviceRegistry;
-
-    @Test
-    void export() throws IOException {
-        new SwaggerExporter(serviceRegistry)
-                .export(SwaggerService.Visibility.PRIVATE, SwaggerService.Format.JSON);
-    }
-}
 
 
 == Hints-n-Tips
diff --git a/testing/integtestsupport/adoc/modules/integtestsupport/pages/hints-and-tips.adoc b/testing/integtestsupport/adoc/modules/integtestsupport/pages/hints-and-tips.adoc
new file mode 100644
index 0000000000..56b4b4087b
--- /dev/null
+++ b/testing/integtestsupport/adoc/modules/integtestsupport/pages/hints-and-tips.adoc
@@ -0,0 +1,11 @@
+= Hints and Tips
+
+:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
+
+== If using JDO/DataNucleus
+
+When running integration tests through the IDE, and if using the JDO/DataNucleus ORM, then make sure the module(s) with entities have been enhanced first.
+
+If using IntelliJ, we recommend creating a run Maven configuration that runs `datanucleus:enhance`, and then pinning the configuration once run as a tab in the "Debug" window for easy access.
+
+image::pin-enhance-run-configuration.png[width="400px"]
diff --git a/testing/integtestsupport/adoc/modules/integtestsupport/pages/influencing-the-interaction.adoc b/testing/integtestsupport/adoc/modules/integtestsupport/pages/influencing-the-interaction.adoc
new file mode 100644
index 0000000000..dba08f6bfa
--- /dev/null
+++ b/testing/integtestsupport/adoc/modules/integtestsupport/pages/influencing-the-interaction.adoc
@@ -0,0 +1,94 @@
+= Influencing the Interaction
+
+:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
+
+By default integration tests are run as the built-in "__system" user.
+But sometimes you need control over the user that executes the test matters, and so you want to specify a different user to perform the test (or change their roles, or change the time, or their locale).
+
+The framework provides a number of options.
+
+== Using `SudoService` or `InteractionService` directly
+
+The most straightforward option is to use the xref:refguide:applib:index/services/sudo/SudoService.adoc[SudoService] to specify the user to interact with:
+
+[source,java]
+----
+sudoService.sudo(
+    InteractionContext.switchUser(UserMemento.ofName("joe")),
+    () -> wrap(toDoItem).completed()
+);
+----
+
+Alternatively, you can achieve much the same thing using xref:refguide:applib:index/services/iactnlayer/InteractionService.adoc[InteractionService], which is a slightly lower-level but more powerful service:
+
+[source,java]
+----
+interactionService.run(
+        InteractionContext.builder().user(UserMemento.ofName("joe")).build(),
+        () -> wrap(toDoItem).completed()
+);
+----
+
+== Using `@InteractAs`
+
+Rather than making an imperative call, you can use the `@InteractAs` annotation to declaratively indicate the user to run the test as.
+
+[source,java]
+----
+@InteractAs(userName = "joe")
+@Test
+public void happy_case(){
+    // ...
+}
+----
+
+== Using the `NoPermissionChecks` JUnit 5 extension
+
+If your integration test references secman, then this will activate secman's authorizor.
+Unless you have set up a seed service, chances are that the default "__system" user will have no permissions and so any calls through the xref:refguide:applib:index/services/wrapper/WrapperFactory.adoc[WrapperFactory] will likely fail.
+
+However, any interaction made by a user with the xref:refguide:applib:index/services/sudo/SudoService.adoc[SudoService]'s xref:refguide:applib:index/services/sudo/SudoService.adoc#ACCESS_ALL_ROLE[ACCESS_ALL_ROLE] role skips permissions checks.
+
+The `NoPermissionChecks` JUnit 5 extension ensures that the user has this role, effectively disabling permission checks even if secman is part of the app.
+
+[source,java]
+----
+@ExtendWith(NoPermissionChecks.class)
+public class MyIntegrationTest extends IsisIntegrationTestAbstract {
+    // ...
+}
+----
+
+== Using the `UserMementoRefiners` JUnit 5 extension
+
+The `UserMementoRefiner` SPI is part of the authentication process, allowing the user memento representing the logged-on user to be tweaked.
+However, this SPI is not normally called during integration tests, because the authentication process is skipped (tests start executing "under the skin", so to speak).
+
+The `UserMementoRefiners` JUnit 5 extension allows any available `UserMementoRefiner` implementations to be called.
+Normally these would be defined within the TestApp used to bootstrap the integration test.
+
+For example:
+
+[source,java]
+----
+@ExtendWith({UserMementoRefiners.class})
+public class MyIntegTest extends IsisIntegrationTestAbstract {
+
+    @SpringBootConfiguration
+    @EnableAutoConfiguration
+    @Import({
+            // ...
+            MyModule.class
+    })
+    public static class TestApp {
+
+        @Bean
+        public UserMementoRefiner userMementoRefiner() {
+            return userMemento -> userMemento.withRoleAdded(
+                                    SudoService.ACCESS_ALL_ROLE.getName()); // <.>
+        }
+    }
+----
+<.> has the same effect as the `NoPermissionsCheck` extension, discussed earlier.
+
+
diff --git a/testing/integtestsupport/adoc/modules/integtestsupport/partials/module-nav.adoc b/testing/integtestsupport/adoc/modules/integtestsupport/partials/module-nav.adoc
index ddda72d9e5..42a4d69655 100644
--- a/testing/integtestsupport/adoc/modules/integtestsupport/partials/module-nav.adoc
+++ b/testing/integtestsupport/adoc/modules/integtestsupport/partials/module-nav.adoc
@@ -2,5 +2,8 @@
 
 
 * xref:testing:integtestsupport:about.adoc[Integ Test Support]
+** xref:testing:integtestsupport:influencing-the-interaction.adoc[Influencing the Interaction]
 ** xref:testing:integtestsupport:domain-model-validator.adoc[Domain Model Validator]
 ** xref:testing:integtestsupport:swagger-exporter.adoc[Swagger Exporter]
+** xref:testing:integtestsupport:hints-and-tips.adoc[Hints and Tips]
+
diff --git a/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/NoPermissionChecks.java b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/NoPermissionChecks.java
new file mode 100644
index 0000000000..c3af7417d0
--- /dev/null
+++ b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/NoPermissionChecks.java
@@ -0,0 +1,40 @@
+package org.apache.isis.testing.integtestsupport.applib;
+
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import org.apache.isis.applib.services.sudo.SudoService;
+
+import lombok.val;
+
+/**
+ * Use to execute integration tests with permission checking disabled, with a user that has
+ * the {@link SudoService#ACCESS_ALL_ROLE ACCESS_ALL_ROLE} role.
+ *
+ * <p>
+ * This can be useful for example if working with code that depends on the secman extension,
+ * where normally this activates the secman authorizor necessitating users and roles to be seeded and then
+ * to run interact using an appropriate user.  Instead of all that, this extension effectively just
+ * disables permission checking.
+ * </p>
+ *
+ * <p>
+ *     To use, annotate integration test class using <code>@ExtendWith(NoPermissionChecks.class)</code>
+ * </p>
+ */
+public class NoPermissionChecks implements BeforeEachCallback {
+
+    @Override
+    public void beforeEach(final ExtensionContext extensionContext) {
+        _Helper.getInteractionFactory(extensionContext)
+                .ifPresent(interactionService ->
+                        interactionService.currentInteractionContext().ifPresent(
+                                currentInteractionContext -> {
+                                    val sudoUser = currentInteractionContext.getUser().withRoleAdded(SudoService.ACCESS_ALL_ROLE.getName());
+                                    interactionService.openInteraction(currentInteractionContext.withUser(sudoUser));
+                                }
+                        )
+                );
+    }
+
+}
diff --git a/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/UserMementoRefiners.java b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/UserMementoRefiners.java
new file mode 100644
index 0000000000..580e51683c
--- /dev/null
+++ b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/UserMementoRefiners.java
@@ -0,0 +1,44 @@
+package org.apache.isis.testing.integtestsupport.applib;
+
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import org.apache.isis.applib.services.sudo.SudoService;
+import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.core.security.authentication.manager.UserMementoRefiner;
+
+/**
+ * Use to execute integration tests where any {@link UserMementoRefiner} services are honoured.
+ * These can be used to tweak the current user/role.  (Normally {@link UserMementoRefiner}s are only
+ * consulted using the authentication process, but in integration tests the authentication phase is skipped).
+ *
+ * <p>
+ * This can be useful for various use cases, though one use case is as an alternative to using the
+ * {@link NoPermissionChecks} extension.
+ * </p>
+ *
+ * <p>
+ *     To use, annotate integration test class using <code>@ExtendWith(UserMementoRefiners.class)</code>
+ * </p>
+ */
+public class UserMementoRefiners implements BeforeEachCallback {
+
+    @Override
+    public void beforeEach(final ExtensionContext extensionContext) {
+        _Helper.getInteractionFactory(extensionContext)
+            .ifPresent(interactionService ->
+                interactionService.currentInteractionContext().ifPresent(
+                    currentInteractionContext -> _Helper.getServiceRegistry(extensionContext).ifPresent(
+                        serviceRegistry -> {
+                            UserMemento user = currentInteractionContext.getUser();
+                            for (UserMementoRefiner userMementoRefiner : serviceRegistry.select(UserMementoRefiner.class)) {
+                                user = userMementoRefiner.refine(user);
+                            }
+                            interactionService.openInteraction(currentInteractionContext.withUser(user));
+                        }
+                    )
+                )
+            );
+    }
+
+}