From e8fe12e92a217e25072fa7cf4874169640828c8b Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Mon, 16 May 2022 23:09:04 +0200
Subject: [PATCH] Fixed #678 - add link to created/edited item in successful
 save banner

---
 .../accounts/AccountController.java           |  6 +-
 .../categories/CategoryController.java        | 10 ++-
 .../budgetmaster/charts/ChartController.java  | 13 +++-
 .../TemplateGroupController.java              | 10 ++-
 .../templates/TemplateController.java         | 19 ++++--
 .../templates/TemplateService.java            |  4 +-
 .../transactions/TransactionController.java   | 14 ++--
 .../notification/NotificationLinkBuilder.java | 26 +++++++
 .../src/main/resources/static/css/style.css   |  4 ++
 .../unit/NotificationLinkBuilderTest.java     | 68 +++++++++++++++++++
 10 files changed, 150 insertions(+), 24 deletions(-)
 create mode 100644 BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/notification/NotificationLinkBuilder.java
 create mode 100644 BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/NotificationLinkBuilderTest.java

diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java
index 3021685d1..cb3fca9f2 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java
@@ -7,6 +7,7 @@ import de.deadlocker8.budgetmaster.utils.Mappings;
 import de.deadlocker8.budgetmaster.utils.ResourceNotFoundException;
 import de.deadlocker8.budgetmaster.utils.WebRequestUtils;
 import de.deadlocker8.budgetmaster.utils.notification.Notification;
+import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder;
 import de.deadlocker8.budgetmaster.utils.notification.NotificationType;
 import de.thecodelabs.utils.util.Localization;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -183,7 +184,7 @@ public class AccountController extends BaseController
 		if(isNewAccount)
 		{
 			account.setType(AccountType.CUSTOM);
-			accountService.getRepository().save(account);
+			account = accountService.getRepository().save(account);
 		}
 		else
 		{
@@ -195,7 +196,8 @@ public class AccountController extends BaseController
 			return ReturnValues.IMPORT_STEP_2;
 		}
 
-		WebRequestUtils.putNotification(webRequest, new Notification(Localization.getString("notification.account.save.success", account.getName()), NotificationType.SUCCESS));
+		final String link = NotificationLinkBuilder.buildEditLink(request, account.getName(), Mappings.ACCOUNTS, account.getID());
+		WebRequestUtils.putNotification(webRequest, new Notification(Localization.getString("notification.account.save.success", link), NotificationType.SUCCESS));
 		return ReturnValues.REDIRECT_SHOW_ALL;
 	}
 
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/categories/CategoryController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/categories/CategoryController.java
index 46d91d048..ef1603db0 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/categories/CategoryController.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/categories/CategoryController.java
@@ -5,6 +5,7 @@ import de.deadlocker8.budgetmaster.icon.IconService;
 import de.deadlocker8.budgetmaster.services.HelpersService;
 import de.deadlocker8.budgetmaster.utils.*;
 import de.deadlocker8.budgetmaster.utils.notification.Notification;
+import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder;
 import de.deadlocker8.budgetmaster.utils.notification.NotificationType;
 import de.thecodelabs.utils.util.ColorUtilsNonJavaFX;
 import de.thecodelabs.utils.util.Localization;
@@ -15,6 +16,7 @@ import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.WebRequest;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.List;
 import java.util.Optional;
 
@@ -131,7 +133,8 @@ public class CategoryController extends BaseController
 	}
 
 	@PostMapping(value = "/newCategory")
-	public String post(WebRequest request,
+	public String post(HttpServletRequest servletRequest,
+					   WebRequest request,
 					   Model model, @ModelAttribute("NewCategory") Category category, BindingResult bindingResult,
 					   @RequestParam(value = "iconImageID", required = false) Integer iconImageID,
 					   @RequestParam(value = "builtinIconIdentifier", required = false) String builtinIconIdentifier,
@@ -155,9 +158,10 @@ public class CategoryController extends BaseController
 			category.setType(CategoryType.CUSTOM);
 		}
 
-		categoryService.save(category);
+		category = categoryService.save(category);
 
-		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.category.save.success", category.getName()), NotificationType.SUCCESS));
+		final String link = NotificationLinkBuilder.buildEditLink(servletRequest, category.getName(), Mappings.CATEGORIES, category.getID());
+		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.category.save.success", link), NotificationType.SUCCESS));
 
 		return ReturnValues.REDIRECT_SHOW_ALL;
 	}
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/charts/ChartController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/charts/ChartController.java
index 7b30e3673..da43e1e44 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/charts/ChartController.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/charts/ChartController.java
@@ -14,6 +14,7 @@ import de.deadlocker8.budgetmaster.utils.Mappings;
 import de.deadlocker8.budgetmaster.utils.ResourceNotFoundException;
 import de.deadlocker8.budgetmaster.utils.WebRequestUtils;
 import de.deadlocker8.budgetmaster.utils.notification.Notification;
+import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder;
 import de.deadlocker8.budgetmaster.utils.notification.NotificationType;
 import de.thecodelabs.utils.util.Localization;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -23,6 +24,7 @@ import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.WebRequest;
 
+import javax.servlet.http.HttpServletRequest;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
@@ -179,7 +181,11 @@ public class ChartController extends BaseController
 	}
 
 	@PostMapping(value = "/newChart")
-	public String post(WebRequest request, Model model, @ModelAttribute("NewChart") Chart chart, BindingResult bindingResult)
+	public String post(HttpServletRequest servletRequest,
+					   WebRequest request,
+					   Model model,
+					   @ModelAttribute("NewChart") Chart chart,
+					   BindingResult bindingResult)
 	{
 		ChartValidator userValidator = new ChartValidator();
 		userValidator.validate(chart, bindingResult);
@@ -211,8 +217,9 @@ public class ChartController extends BaseController
 			}
 		}
 
-		chartService.getRepository().save(chart);
-		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.chart.save.success", chart.getName()), NotificationType.SUCCESS));
+		chart = chartService.getRepository().save(chart);
+		final String link = NotificationLinkBuilder.buildEditLink(servletRequest, chart.getName(), Mappings.CHARTS, chart.getID());
+		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.chart.save.success", link), NotificationType.SUCCESS));
 		return ReturnValues.REDIRECT_MANAGE;
 	}
 
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templategroup/TemplateGroupController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templategroup/TemplateGroupController.java
index 1aa6c81a7..da470d698 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templategroup/TemplateGroupController.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templategroup/TemplateGroupController.java
@@ -8,6 +8,7 @@ import de.deadlocker8.budgetmaster.utils.Mappings;
 import de.deadlocker8.budgetmaster.utils.ResourceNotFoundException;
 import de.deadlocker8.budgetmaster.utils.WebRequestUtils;
 import de.deadlocker8.budgetmaster.utils.notification.Notification;
+import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder;
 import de.deadlocker8.budgetmaster.utils.notification.NotificationType;
 import de.thecodelabs.utils.util.Localization;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -17,6 +18,7 @@ import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.WebRequest;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.Optional;
 
 
@@ -90,7 +92,8 @@ public class TemplateGroupController extends BaseController
 	}
 
 	@PostMapping(value = "/newTemplateGroup")
-	public String post(WebRequest request,
+	public String post(HttpServletRequest servletRequest,
+					   WebRequest request,
 					   Model model,
 					   @ModelAttribute("NewTemplateGroup") TemplateGroup templateGroup, BindingResult bindingResult)
 	{
@@ -108,9 +111,10 @@ public class TemplateGroupController extends BaseController
 
 		templateGroup.setType(TemplateGroupType.CUSTOM);
 
-		templateGroupService.getRepository().save(templateGroup);
+		templateGroup = templateGroupService.getRepository().save(templateGroup);
 
-		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.template.save.success", templateGroup.getName()), NotificationType.SUCCESS));
+		final String link = NotificationLinkBuilder.buildEditLink(servletRequest, templateGroup.getName(), Mappings.TEMPLATE_GROUPS, templateGroup.getID());
+		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.template.save.success", link), NotificationType.SUCCESS));
 		return ReturnValues.REDIRECT_ALL_ENTITIES;
 	}
 
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateController.java
index 1592aefe7..a868be082 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateController.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateController.java
@@ -13,6 +13,7 @@ import de.deadlocker8.budgetmaster.utils.Mappings;
 import de.deadlocker8.budgetmaster.utils.ResourceNotFoundException;
 import de.deadlocker8.budgetmaster.utils.WebRequestUtils;
 import de.deadlocker8.budgetmaster.utils.notification.Notification;
+import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder;
 import de.deadlocker8.budgetmaster.utils.notification.NotificationType;
 import de.thecodelabs.utils.util.Localization;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.WebRequest;
 import org.springframework.web.server.ResponseStatusException;
 
+import javax.servlet.http.HttpServletRequest;
 import java.time.LocalDate;
 import java.util.Optional;
 
@@ -80,7 +82,8 @@ public class TemplateController extends BaseController
 	}
 
 	@PostMapping(value = "/fromTransaction")
-	public String postFromTransaction(WebRequest request,
+	public String postFromTransaction(HttpServletRequest servletRequest,
+									  WebRequest request,
 									  @RequestParam(value = "templateName") String templateName,
 									  @ModelAttribute("NewTransaction") Transaction transaction,
 									  @RequestParam(value = "includeCategory") Boolean includeCategory,
@@ -97,9 +100,10 @@ public class TemplateController extends BaseController
 			throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "templateName must not be empty");
 		}
 
-		templateService.createFromTransaction(templateName, transaction, includeCategory, includeAccount);
+		final Template newTemplate = templateService.createFromTransaction(templateName, transaction, includeCategory, includeAccount);
 
-		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.template.add.success", templateName), NotificationType.SUCCESS));
+		final String link = NotificationLinkBuilder.buildEditLink(servletRequest, newTemplate.getTemplateName(), Mappings.TEMPLATES, newTemplate.getID());
+		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.template.add.success", link), NotificationType.SUCCESS));
 
 		return ReturnValues.REDIRECT_ALL_ENTITIES;
 	}
@@ -178,7 +182,8 @@ public class TemplateController extends BaseController
 	}
 
 	@PostMapping(value = "/newTemplate")
-	public String post(WebRequest request,
+	public String post(HttpServletRequest servletRequest,
+					   WebRequest request,
 					   Model model,
 					   @ModelAttribute("NewTemplate") Template template, BindingResult bindingResult,
 					   @RequestParam(value = "includeAccount", required = false) boolean includeAccount,
@@ -238,8 +243,10 @@ public class TemplateController extends BaseController
 			template.setTransferAccount(null);
 		}
 
-		templateService.getRepository().save(template);
-		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.template.save.success", template.getName()), NotificationType.SUCCESS));
+		template = templateService.getRepository().save(template);
+
+		final String link = NotificationLinkBuilder.buildEditLink(servletRequest, template.getTemplateName(), Mappings.CHARTS, template.getID());
+		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.template.save.success", link), NotificationType.SUCCESS));
 		return ReturnValues.REDIRECT_ALL_ENTITIES;
 	}
 
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateService.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateService.java
index c6fa79bb6..75a80443d 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateService.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateService.java
@@ -89,7 +89,7 @@ public class TemplateService implements Resettable, AccessAllEntities<Template>,
 		}
 	}
 
-	public void createFromTransaction(String templateName, Transaction transaction, boolean includeCategory, boolean includeAccount)
+	public Template createFromTransaction(String templateName, Transaction transaction, boolean includeCategory, boolean includeAccount)
 	{
 		final Template template = new Template(templateName, transaction);
 		if(!includeCategory)
@@ -104,7 +104,7 @@ public class TemplateService implements Resettable, AccessAllEntities<Template>,
 
 		template.setTemplateGroup(templateGroupService.getDefaultGroup());
 
-		getRepository().save(template);
+		return getRepository().save(template);
 	}
 
 	public void prepareTemplateForNewTransaction(TransactionBase template, boolean prepareAccount)
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
index d0ee062c3..3c0eb8f48 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
@@ -22,6 +22,7 @@ import de.deadlocker8.budgetmaster.utils.Mappings;
 import de.deadlocker8.budgetmaster.utils.ResourceNotFoundException;
 import de.deadlocker8.budgetmaster.utils.WebRequestUtils;
 import de.deadlocker8.budgetmaster.utils.notification.Notification;
+import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder;
 import de.deadlocker8.budgetmaster.utils.notification.NotificationType;
 import de.thecodelabs.utils.util.Localization;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -148,7 +149,8 @@ public class TransactionController extends BaseController
 	}
 
 	@PostMapping(value = "/newTransaction")
-	public String post(WebRequest request,
+	public String post(HttpServletRequest servletRequest,
+					   WebRequest request,
 					   Model model, @CookieValue("currentDate") String cookieDate,
 					   @ModelAttribute("NewTransaction") Transaction transaction, BindingResult bindingResult,
 					   @RequestParam(value = "isRepeating", required = false) boolean isRepeating,
@@ -190,7 +192,7 @@ public class TransactionController extends BaseController
 
 
 		final boolean isContinueActivated = action.equals(CONTINUE);
-		return handleRedirect(request, model, transaction.getID() != null, transaction, bindingResult, date, redirectUrl, isContinueActivated);
+		return handleRedirect(servletRequest, request, model, transaction.getID() != null, transaction, bindingResult, date, redirectUrl, isContinueActivated);
 	}
 
 	private void handlePreviousType(Transaction transaction, boolean isRepeating)
@@ -226,7 +228,7 @@ public class TransactionController extends BaseController
 		return new RepeatingOption(startDate, repeatingModifier, repeatingEnd);
 	}
 
-	private String handleRedirect(WebRequest request, Model model, boolean isEdit, @ModelAttribute("NewTransaction") Transaction transaction, BindingResult bindingResult, LocalDate date, String url, boolean isContinueActivated)
+	private String handleRedirect(HttpServletRequest servletRequest, WebRequest request, Model model, boolean isEdit, @ModelAttribute("NewTransaction") Transaction transaction, BindingResult bindingResult, LocalDate date, String url, boolean isContinueActivated)
 	{
 		if(bindingResult.hasErrors())
 		{
@@ -235,8 +237,10 @@ public class TransactionController extends BaseController
 			return url;
 		}
 
-		transactionService.getRepository().save(transaction);
-		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.transaction.save.success", transaction.getName()), NotificationType.SUCCESS));
+		transaction = transactionService.getRepository().save(transaction);
+
+		final String link = NotificationLinkBuilder.buildEditLink(servletRequest, transaction.getName(), Mappings.TRANSACTIONS, transaction.getID());
+		WebRequestUtils.putNotification(request, new Notification(Localization.getString("notification.transaction.save.success", link), NotificationType.SUCCESS));
 
 		if(isContinueActivated)
 		{
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/notification/NotificationLinkBuilder.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/notification/NotificationLinkBuilder.java
new file mode 100644
index 000000000..f9c344189
--- /dev/null
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/notification/NotificationLinkBuilder.java
@@ -0,0 +1,26 @@
+package de.deadlocker8.budgetmaster.utils.notification;
+
+import javax.servlet.http.HttpServletRequest;
+import java.text.MessageFormat;
+
+public class NotificationLinkBuilder
+{
+	private NotificationLinkBuilder()
+	{
+		// empty
+	}
+
+	public static String buildEditLink(HttpServletRequest request, String linkName, String relativeLinkPath, Integer id)
+	{
+		final String contextPath = request.getContextPath();
+
+		return MessageFormat.format("<a href=\"{0}{1}/{2}/edit\" class=\"text-default\">{3}</a>", contextPath, relativeLinkPath, String.valueOf(id), linkName);
+	}
+
+	public static String build(HttpServletRequest request, String linkName, String relativeLinkPath)
+	{
+		final String contextPath = request.getContextPath();
+
+		return MessageFormat.format("<a href=\"{0}{1}\">{2}</a>", contextPath, relativeLinkPath, linkName);
+	}
+}
diff --git a/BudgetMasterServer/src/main/resources/static/css/style.css b/BudgetMasterServer/src/main/resources/static/css/style.css
index 3c82c7084..04ff08070 100644
--- a/BudgetMasterServer/src/main/resources/static/css/style.css
+++ b/BudgetMasterServer/src/main/resources/static/css/style.css
@@ -248,6 +248,10 @@ main {
     cursor: pointer;
 }
 
+.notification-item a {
+    text-decoration: underline;
+}
+
 .break-all {
     word-break: break-all;
 }
diff --git a/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/NotificationLinkBuilderTest.java b/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/NotificationLinkBuilderTest.java
new file mode 100644
index 000000000..d53424b24
--- /dev/null
+++ b/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/NotificationLinkBuilderTest.java
@@ -0,0 +1,68 @@
+package de.deadlocker8.budgetmaster.unit;
+
+import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+class NotificationLinkBuilderTest
+{
+	@Test
+	void test_buildEditLink_noContextPath()
+	{
+		HttpServletRequest mockedRequest = mock(HttpServletRequest.class);
+
+		Mockito.when(mockedRequest.getContextPath()).thenReturn("");
+
+		assertThat(NotificationLinkBuilder.buildEditLink(mockedRequest, "My Link", "/accounts", 15))
+				.isEqualTo("<a href=\"/accounts/15/edit\" class=\"text-default\">My Link</a>");
+	}
+
+	@Test
+	void test_buildEditLink_withContextPath()
+	{
+		HttpServletRequest mockedRequest = mock(HttpServletRequest.class);
+
+		Mockito.when(mockedRequest.getContextPath()).thenReturn("/contextMatters");
+
+		assertThat(NotificationLinkBuilder.buildEditLink(mockedRequest, "My Link", "/accounts", 15))
+				.isEqualTo("<a href=\"/contextMatters/accounts/15/edit\" class=\"text-default\">My Link</a>");
+	}
+
+	@Test
+	void test_buildEditLink_largeID()
+	{
+		HttpServletRequest mockedRequest = mock(HttpServletRequest.class);
+
+		Mockito.when(mockedRequest.getContextPath()).thenReturn("");
+
+		assertThat(NotificationLinkBuilder.buildEditLink(mockedRequest, "My Link", "/accounts", 123456))
+				.isEqualTo("<a href=\"/accounts/123456/edit\" class=\"text-default\">My Link</a>");
+	}
+
+	@Test
+	void test_build_noContextPath()
+	{
+		HttpServletRequest mockedRequest = mock(HttpServletRequest.class);
+
+		Mockito.when(mockedRequest.getContextPath()).thenReturn("");
+
+		assertThat(NotificationLinkBuilder.build(mockedRequest, "My Link", "/accounts/0815"))
+				.isEqualTo("<a href=\"/accounts/0815\">My Link</a>");
+	}
+
+	@Test
+	void test_build_withContextPath()
+	{
+		HttpServletRequest mockedRequest = mock(HttpServletRequest.class);
+
+		Mockito.when(mockedRequest.getContextPath()).thenReturn("/contextMatters");
+
+		assertThat(NotificationLinkBuilder.build(mockedRequest, "My Link", "/accounts/0815"))
+				.isEqualTo("<a href=\"/contextMatters/accounts/0815\">My Link</a>");
+	}
+}
-- 
GitLab