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 2021/06/11 12:31:30 UTC
[isis] 07/08: ISIS-2732: adds docs on mocking clock and user
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch ISIS-2732
in repository https://gitbox.apache.org/repos/asf/isis.git
commit 7f0c0b88cf197aa08a1ddb6953becc5ae3e3d12c
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Jun 11 13:12:57 2021 +0100
ISIS-2732: adds docs on mocking clock and user
---
.../services/command/CommandExecutorService.java | 24 +++----
.../services/iactnlayer/InteractionContext.java | 59 ++++++++++++++++
.../InteractionContext_combine_Test.java | 25 +++++++
.../command/CommandExecutorServiceDefault.java | 18 ++---
.../jobcallables/ReplicateAndRunCommands.java | 2 +-
.../adoc/modules/fixtures/pages/about.adoc | 6 +-
.../fixture-scripts/mocking-the-clock-or-user.adoc | 79 ++++++++++++++++++++++
.../pages/fixture-scripts/mocking-the-clock.adoc | 49 --------------
.../pages/fixture-scripts/sudo-service.adoc | 55 ---------------
9 files changed, 187 insertions(+), 130 deletions(-)
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandExecutorService.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandExecutorService.java
index fceb8f2..bae583d 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandExecutorService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandExecutorService.java
@@ -47,7 +47,7 @@ public interface CommandExecutorService {
*
* @since 1.x {@index}
*/
- enum SudoPolicy {
+ enum InteractionContextPolicy {
/**
* Execute within the same {@link org.apache.isis.applib.services.iactnlayer.InteractionContext} as the
@@ -89,26 +89,26 @@ public interface CommandExecutorService {
public final BiFunction<InteractionContext, CommandDto, InteractionContext> mapper;
- private SudoPolicy(BiFunction<InteractionContext, CommandDto, InteractionContext> mapper) {
+ private InteractionContextPolicy(BiFunction<InteractionContext, CommandDto, InteractionContext> mapper) {
this.mapper = mapper;
}
}
/**
- * Executes the specified {@link Command} using the required {@link SudoPolicy}, updating the Command (or its
+ * Executes the specified {@link Command} using the required {@link InteractionContextPolicy}, updating the Command (or its
* persistent equivalent) afterwards (for example, setting its {@link Command#getCommandDto() commandDto} field.
*
- * @param sudoPolicy - policy to use
+ * @param interactionContextPolicy - policy to use
* @param command - the {@link Command} to be executed
* @return - a bookmark representing the result of executing the command (could be null)
*/
Bookmark executeCommand(
- SudoPolicy sudoPolicy,
+ InteractionContextPolicy interactionContextPolicy,
Command command
);
/**
- * Executes the specified command (represented as a {@link CommandDto} using the required {@link SudoPolicy}.
+ * Executes the specified command (represented as a {@link CommandDto} using the required {@link InteractionContextPolicy}.
*
* <p>
* IMPORTANT: THIS METHOD HAS SIGNIFICANT SIDE-EFFECTS. Specifically, the {@link Command} of the executing
@@ -122,38 +122,38 @@ public interface CommandExecutorService {
* used by the persistent implementations to update their respective persistent equivalents of {@link Command}.
* </p>
*
- * @param sudoPolicy - policy to use
+ * @param interactionContextPolicy - policy to use
* @param commandDto - the {@link CommandDto} to be executed
* @param outcomeHandler - callback to handle the result
*
* @return - a bookmark representing the result of executing the command (could be null)
*/
Bookmark executeCommand(
- SudoPolicy sudoPolicy,
+ InteractionContextPolicy interactionContextPolicy,
CommandDto commandDto,
CommandOutcomeHandler outcomeHandler);
/**
- * As per {@link #executeCommand(SudoPolicy, Command)}, with a policy of {@link SudoPolicy#NO_SWITCH no switch}.
+ * As per {@link #executeCommand(InteractionContextPolicy, Command)}, with a policy of {@link InteractionContextPolicy#NO_SWITCH no switch}.
*
* <p>
* Note that this method updates the Command as a side-effect.
* </p>
*
- * @see #executeCommand(SudoPolicy, Command)
+ * @see #executeCommand(InteractionContextPolicy, Command)
*/
Bookmark executeCommand(
Command command
);
/**
- * As per {@link #executeCommand(SudoPolicy, CommandDto, CommandOutcomeHandler)}, with a policy of {@link SudoPolicy#NO_SWITCH no switch}.
+ * As per {@link #executeCommand(InteractionContextPolicy, CommandDto, CommandOutcomeHandler)}, with a policy of {@link InteractionContextPolicy#NO_SWITCH no switch}.
*
* <p>
* Note that this method has significant side-effects.
* </p>
*
- * @see #executeCommand(SudoPolicy, CommandDto, CommandOutcomeHandler)
+ * @see #executeCommand(InteractionContextPolicy, CommandDto, CommandOutcomeHandler)
*
* @param commandDto
* @param outcomeHandler
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
index 394de8f..1d75cb7 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
@@ -21,6 +21,8 @@ package org.apache.isis.applib.services.iactnlayer;
import java.io.Serializable;
import java.util.Locale;
import java.util.TimeZone;
+import java.util.function.UnaryOperator;
+import java.util.stream.Stream;
import org.apache.isis.applib.clock.VirtualClock;
import org.apache.isis.applib.services.iactn.Interaction;
@@ -86,6 +88,63 @@ public class InteractionContext implements Serializable {
@NonNull TimeZone timeZone = TimeZone.getDefault();
+ /**
+ * Convenience method for use with {@link org.apache.isis.applib.services.sudo.SudoService}, returning a
+ * {@link UnaryOperator} that will act upon the provided {@link InteractionContext} to return the same but with
+ * the specified {@link UserMemento}.
+ */
+ public static UnaryOperator<InteractionContext> switchUser(@NonNull final UserMemento userMemento) {
+ return interactionContext -> interactionContext.withUser(userMemento);
+ }
+
+ /**
+ * Convenience method for use with {@link org.apache.isis.applib.services.sudo.SudoService}, returning a
+ * {@link UnaryOperator} that will act upon the provided {@link InteractionContext} to return the same but with
+ * the specified {@link VirtualClock}.
+ */
+ public static UnaryOperator<InteractionContext> switchClock(@NonNull final VirtualClock clock) {
+ return interactionContext -> interactionContext.withClock(clock);
+ }
+
+ /**
+ * Convenience method for use with {@link org.apache.isis.applib.services.sudo.SudoService}, returning a
+ * {@link UnaryOperator} that will act upon the provided {@link InteractionContext} to return the same but with
+ * the specified {@link Locale}.
+ */
+ public static UnaryOperator<InteractionContext> switchLocale(@NonNull final Locale locale) {
+ return interactionContext -> interactionContext.withLocale(locale);
+ }
+
+ /**
+ * Convenience method for use with {@link org.apache.isis.applib.services.sudo.SudoService}, returning a
+ * {@link UnaryOperator} that will act upon the provided {@link InteractionContext} to return the same but with
+ * the specified {@link TimeZone}.
+ */
+ public static UnaryOperator<InteractionContext> switchTimeZone(@NonNull final TimeZone timeZone) {
+ return interactionContext -> interactionContext.withTimeZone(timeZone);
+ }
+
+ /**
+ * Convenience method to combine {@link UnaryOperator}s, for example as per {@link #switchUser(UserMemento)} and {@link #switchTimeZone(TimeZone)}.
+ *
+ * <p>
+ * NOTE: this implementation can result in heap pollution; better to use the {@link #combine(Stream) overload}.
+ * </p>
+ *
+ * @see #combine(Stream)
+ */
+ public static <T> UnaryOperator<T> combine(UnaryOperator<T>... mappers) {
+ return combine(Stream.of(mappers));
+ }
+
+ /**
+ * Convenience method to combine {@link UnaryOperator}s, for example as per {@link #switchUser(UserMemento)} and {@link #switchTimeZone(TimeZone)}.
+ *
+ * credit: https://stackoverflow.com/a/51065029/56880
+ */
+ public static <T> UnaryOperator<T> combine(Stream<UnaryOperator<T>> mappers) {
+ return mappers.reduce(t -> t, (a,b) -> a.andThen(b)::apply);
+ }
}
diff --git a/api/applib/src/test/java/org/apache/isis/applib/services/iactnlayer/InteractionContext_combine_Test.java b/api/applib/src/test/java/org/apache/isis/applib/services/iactnlayer/InteractionContext_combine_Test.java
new file mode 100644
index 0000000..2cb9675
--- /dev/null
+++ b/api/applib/src/test/java/org/apache/isis/applib/services/iactnlayer/InteractionContext_combine_Test.java
@@ -0,0 +1,25 @@
+package org.apache.isis.applib.services.iactnlayer;
+
+import java.util.function.UnaryOperator;
+import java.util.stream.Stream;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import lombok.val;
+
+class InteractionContext_combine_Test {
+
+ @Test
+ void happy_case() {
+
+ val mappers = Stream.<UnaryOperator<String>>of(
+ s -> s + "bar",
+ s -> "[" + s + "]",
+ s -> s + s);
+ val result = InteractionContext.combine(mappers).apply("foo");
+
+ Assertions.assertThat(result).isEqualTo("[foobar][foobar]");
+ }
+
+}
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
index 13b11d3..d452737 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
@@ -102,15 +102,15 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
@Override
public Bookmark executeCommand(final Command command) {
- return executeCommand(SudoPolicy.NO_SWITCH, command);
+ return executeCommand(InteractionContextPolicy.NO_SWITCH, command);
}
@Override
public Bookmark executeCommand(
- final SudoPolicy sudoPolicy,
+ final InteractionContextPolicy interactionContextPolicy,
final Command command) {
- return doExecute(sudoPolicy, command.getCommandDto(), command.updater());
+ return doExecute(interactionContextPolicy, command.getCommandDto(), command.updater());
}
@Override
@@ -118,20 +118,20 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
final CommandDto dto,
final CommandOutcomeHandler outcomeHandler) {
- return executeCommand(SudoPolicy.NO_SWITCH, dto, outcomeHandler);
+ return executeCommand(InteractionContextPolicy.NO_SWITCH, dto, outcomeHandler);
}
@Override
public Bookmark executeCommand(
- final SudoPolicy sudoPolicy,
+ final InteractionContextPolicy interactionContextPolicy,
final CommandDto dto,
final CommandOutcomeHandler outcomeHandler) {
- return doExecute(sudoPolicy, dto, outcomeHandler);
+ return doExecute(interactionContextPolicy, dto, outcomeHandler);
}
private Bookmark doExecute(
- final SudoPolicy sudoPolicy,
+ final InteractionContextPolicy interactionContextPolicy,
final CommandDto dto,
final CommandOutcomeHandler commandUpdater) {
@@ -145,12 +145,12 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
val result = transactionService.callWithinCurrentTransactionElseCreateNew(
() -> {
- if (sudoPolicy == SudoPolicy.NO_SWITCH) {
+ if (interactionContextPolicy == InteractionContextPolicy.NO_SWITCH) {
// short-circuit
return doExecuteCommand(dto);
}
return sudoService.call(
- context -> sudoPolicy.mapper.apply(context, dto),
+ context -> interactionContextPolicy.mapper.apply(context, dto),
() -> doExecuteCommand(dto));
});
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
index 78d6f24..2d36807 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
@@ -174,7 +174,7 @@ public class ReplicateAndRunCommands implements Callable<SecondaryStatus> {
transactionService.runWithinCurrentTransactionElseCreateNew(
() -> {
commandExecutorService.executeCommand(
- CommandExecutorService.SudoPolicy.SWITCH_USER_AND_TIME, commandJdo.getCommandDto(), commandJdo.outcomeHandler());
+ CommandExecutorService.InteractionContextPolicy.SWITCH_USER_AND_TIME, commandJdo.getCommandDto(), commandJdo.outcomeHandler());
});
transactionService.runWithinCurrentTransactionElseCreateNew(() -> {
diff --git a/testing/fixtures/adoc/modules/fixtures/pages/about.adoc b/testing/fixtures/adoc/modules/fixtures/pages/about.adoc
index 1b0823e..0f428c6 100644
--- a/testing/fixtures/adoc/modules/fixtures/pages/about.adoc
+++ b/testing/fixtures/adoc/modules/fixtures/pages/about.adoc
@@ -17,7 +17,6 @@ The solution that Apache Isis provides is a small library called *_fixture scrip
A fixture script is basically a command object for executing arbitrary work, where the work in question is almost always invoking one or more business actions.
In other words, the database is populating through the functionality of the domain object model itself.
-
There is another benefit to Apache Isis' fixture script approach; the fixtures can be (in prototyping mode) run from your application.
This means that fixture scripts can actually help all the way through the development lifecycle:
@@ -36,13 +35,12 @@ And if you can't write a fixture script for the story, it probably means that th
* when you want to roll out training to your users, you can write fixture scripts as part of their training exercises
-The following sections explain the API and how to go about using the API.
+The following sections explain the API and how to go about using the API.
include::fixture-scripts/api-and-usage.adoc[leveloffset=+1]
-include::fixture-scripts/mocking-the-clock.adoc[leveloffset=+1]
-include::fixture-scripts/sudo-service.adoc[leveloffset=+1]
+include::fixture-scripts/mocking-the-clock-or-user.adoc[leveloffset=+1]
diff --git a/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/mocking-the-clock-or-user.adoc b/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/mocking-the-clock-or-user.adoc
new file mode 100644
index 0000000..53dbda0
--- /dev/null
+++ b/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/mocking-the-clock-or-user.adoc
@@ -0,0 +1,79 @@
+= Mocking the Clock or User
+
+: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 [...]
+
+
+The xref:refguide:applib:index/services/sudo/SudoService.adoc[SudoService] provides the mechanism to run an arbitrary block of code, but changing the effective user executing the block, or the effective time that the block was run.
+
+This is a general purpose capability, but is especially useful for fixtures.
+
+== Mocking the Clock
+
+It's sometimes necessary to change the effective time that a fixture or test runs.
+This can be done by calling xref:refguide:applib:index/services/sudo/SudoService.adoc[SudoService] with an xref:refguide:applib:index/services/iactnlayer/InteractionContext.adoc[InteractionContext] that has switched the clock.
+
+For example:
+
+[source,java]
+----
+val when = VirtualClock.nowAt(Instant.parse("2017-02-03T09:00:00.00Z")); // <.>
+
+simpleObject =
+ sudoService.call(
+ InteractionContext.switchClock(when), // <.>
+ ()-> fixtureScripts.runPersona(SimpleObject_persona.FOO) // <.>
+ );
+----
+
+<.> the effective time, in other words the date/time we want the xref:refguide:applib:index/services/clock/ClockService.adoc[ClockService] to report
+<.> Unary operator on xref:refguide:applib:index/services/iactnlayer/InteractionContext.adoc[InteractionContext] to switch the effective time
+<.> the block of code to run within this modified xref:refguide:applib:index/services/iactnlayer/InteractionContext.adoc[InteractionContext].
+
+
+== Switching the User ("Sudo")
+
+Sometimes in our fixture scripts we want to perform a business action running as a particular user.
+This might be for the usual reason - so that our fixtures accurately reflect the reality of the system with all business constraints enforced using the `WrapperFactory` - or more straightforwardly it might be simply that the action depends on the identity of the user invoking the action.
+Either way, this can be done by calling xref:refguide:applib:index/services/sudo/SudoService.adoc[SudoService] with an xref:refguide:applib:index/services/iactnlayer/InteractionContext.adoc[InteractionContext] that has switched the effective user.
+
+
+For example:
+
+[source,java]
+----
+val who = UserMemento.ofName("fred"); // <.>
+
+simpleObject =
+ sudoService.call(
+ InteractionContext.switchUser(who), // <.>
+ ()-> fixtureScripts.runPersona(SimpleObject_persona.FOO) // <.>
+ );
+----
+
+<.> the effective user, in other words the user that we want xref:refguide:applib:index/services/user/UserService.adoc[UserService] to report
+<.> Unary operator on xref:refguide:applib:index/services/iactnlayer/InteractionContext.adoc[InteractionContext] to switch the effective user
+<.> the block of code to run within this modified xref:refguide:applib:index/services/iactnlayer/InteractionContext.adoc[InteractionContext].
+
+
+
+== Mocking the Clock and the User
+
+If we want to change both the effective time _and_ the effective user, then we just need to combine the ``UnaryOperator``s passed into xref:refguide:applib:index/services/sudo/SudoService.adoc[SudoService].
+
+As a convenience, xref:refguide:applib:index/services/iactnlayer/InteractionContext.adoc[InteractionContext] provides a helper method for just this purpose:
+
+For example:
+
+[source,java]
+----
+val who = UserMemento.ofName("fred");
+val when = VirtualClock.nowAt(Instant.parse("2017-02-03T09:00:00.00Z"));
+
+val switchWhoAndWhen =
+ InteractionContext.combine(
+ InteractionContext.switchUser(who),
+ InteractionContext.switchClock(when)
+ );
+----
+
+This can then be passed in as the first argument to `SudoService`'s xref:refguide:applib:index/services/sudo/SudoService.adoc#call__UnaryOperator_Callable[call(...)] or xref:refguide:applib:index/services/sudo/SudoService.adoc#run__UnaryOperator_ThrowingRunnable[run(...)] methods.
diff --git a/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/mocking-the-clock.adoc b/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/mocking-the-clock.adoc
deleted file mode 100644
index fbfdc5f..0000000
--- a/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/mocking-the-clock.adoc
+++ /dev/null
@@ -1,49 +0,0 @@
-= Mocking the Clock
-
-: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 [...]
-
-
-WARNING: TODO: v2 - this documentation has not yet been updated.
-In particular, the `Clock` singleton has been removed, the `ClockService` has been refactored.
-
-It's often useful to be able to change the effective time that a test runs.
-The framework itself uses the xref:refguide:applib:index/services/clock/ClockService.adoc[ClockService] to obtain the current time, and applications should also use this domain service for a consistent view of the current time.
-
-This page describes how to change the current time, and how to set up tests using fixture scripts with a mocked time.
-
-== FixtureClock
-
-The default xref:refguide:applib:index/services/clock/ClockService.adoc[ClockService] implementation in fact simply delegates to another class defined in the API, namely the `o.a.i.applib.clock.Clock`, an abstract singleton class.
-It is not recommended that your code use the `Clock` directly, but it's worth understanding how this all works.
-
-* If running in xref:refguide:config:about.adoc#deployment-types[production] (server) mode, then the framework will (lazily) instantiate the ``SystemClock` when first required.
-This is a read-only clock that reads from the system time.
-The instance registers itself as the singleton and cannot be replaced.
-
-* If running in xref:refguide:config:about.adoc#deployment-types[prototype] mode, though, then the framework (using `InitialFixtureScriptsInstaller`) will instead instantiate `FixtureClock`.
-This is a read-write clock that will behave as the system clock, unless it is explicitly set using eg, `FixtureClock#setDate(...)` or `FixtureClock#setTime(...)` etc.
-
-Moreover, the `FixtureClock` singleton can be replaced with another implementation.
-And, it is sometimes useful to replace it using `TickingFixtureClock`, a subclass that is very similar to `FixtureClock` (in that the time can be changed) but which will continue to tick once set.
-
-
-== TickingClockFixture
-
-The `TickingClockFixture` is a pre-built fixture script that resets the date/time returned by the xref:refguide:applib:index/services/clock/ClockService.adoc[ClockService] to a known value.
-
-Thereafter the time returned continues to tick forward (as would the real clock) until reset once more.
-
-For example, to set the clock to return "3 Sept 2014", use:
-
-[source,java]
-----
-executionContext.executeChild(this, new TickingClockFixture().setTo("2014-09-03"));
-----
-
-A variety of format strings are supported; the format "YYYY-MM-DD" (as shown above) will work in every locale.
-
-NOTE: This fixture script requires that a `TickingFixtureClock` is initialized during bootstrapping.
-
-
-
-
diff --git a/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/sudo-service.adoc b/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/sudo-service.adoc
deleted file mode 100644
index c8fd420..0000000
--- a/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/sudo-service.adoc
+++ /dev/null
@@ -1,55 +0,0 @@
-= `SudoService`
-
-: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 [...]
-
-
-
-Sometimes in our fixture scripts we want to perform a business action running as a particular user.
-This might be for the usual reason - so that our fixtures accurately reflect the reality of the system with all business constraints enforced using the `WrapperFactory` - or more straightforwardly it might be simply that the action depends on the identity of the user invoking the action.
-
-For example, consider this code that creates a todo item:
-
-[source,java]
-.Production code that depends on current user
-----
-// annotations omitted for clarity
-public ToDoItem newToDo(
- final String description,
- final Category category,
- final Subcategory subcategory,
- final LocalDate dueBy,
- final BigDecimal cost) {
- return newToDo(description, category, subcategory, currentUserName(), dueBy, cost);
-}
-private String currentUserName() {
- return container.getUser().getName(); // <1>
-}
-----
-<1> is the current user.
-
-The fixture for this can use the `SudoService` to run a block of code as a specified user:
-
-[source,java]
-.Fixture Script
-----
-final String description = ...
-final Category category = ...
-final Subcategory subcategory = ...
-final LocalDate dueBy = ...
-final BigDecimal cost = ...
-final Location location = ...
-
-toDoItem = sudoService.sudo(username,
- new Callable<ToDoItem>() {
- @Override
- public ToDoItem call() {
- final ToDoItem toDoItem = wrap(toDoItems).newToDo(
- description, category, subcategory, dueBy, cost);
- wrap(toDoItem).setLocation(location);
- return toDoItem;
- }
- });
-----
-
-Behind the scenes the `SudoService` simply talks to the `UserService` to override the user returned by the `getUser()` API.
-It is possible to override both users and roles.