diff --git a/src/Auth/Process/Accounting.php b/src/Auth/Process/Accounting.php
index 146523a848111bf9fcbdfeefcb425c8a560f8770..a2488982fae8cc4911737a18f8f73005a6547344 100644
--- a/src/Auth/Process/Accounting.php
+++ b/src/Auth/Process/Accounting.php
@@ -10,10 +10,10 @@ use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Entities\Authentication\State;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Services\Logger;
 use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
 use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
-use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
 
 class Accounting extends ProcessingFilter
 {
@@ -21,19 +21,23 @@ class Accounting extends ProcessingFilter
     protected JobsStoreBuilder $jobsStoreBuilder;
     protected LoggerInterface $logger;
     protected AuthenticationDataTrackerBuilder $authenticationDataTrackerBuilder;
+    protected HelpersManager $helpersManager;
 
     /**
      * @param array $config
      * @param mixed $reserved
      * @param ModuleConfiguration|null $moduleConfiguration
      * @param LoggerInterface|null $logger
+     * @param HelpersManager|null $helpersManager
      * @param JobsStoreBuilder|null $jobsStoreBuilder
+     * @param AuthenticationDataTrackerBuilder|null $authenticationDataTrackerBuilder
      */
     public function __construct(
         array &$config,
         $reserved,
         ModuleConfiguration $moduleConfiguration = null,
         LoggerInterface $logger = null,
+        HelpersManager $helpersManager = null,
         JobsStoreBuilder $jobsStoreBuilder = null,
         AuthenticationDataTrackerBuilder $authenticationDataTrackerBuilder = null
     ) {
@@ -41,14 +45,15 @@ class Accounting extends ProcessingFilter
 
         $this->moduleConfiguration = $moduleConfiguration ?? new ModuleConfiguration();
         $this->logger = $logger ?? new Logger();
-        $this->jobsStoreBuilder = $jobsStoreBuilder ?? new JobsStoreBuilder($this->moduleConfiguration, $this->logger);
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
+        $this->jobsStoreBuilder = $jobsStoreBuilder ??
+            new JobsStoreBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
 
         $this->authenticationDataTrackerBuilder = $authenticationDataTrackerBuilder ??
-            new AuthenticationDataTrackerBuilder($this->moduleConfiguration, $this->logger);
+            new AuthenticationDataTrackerBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
     }
 
     /**
-     * @throws StoreException
      */
     public function process(array &$state): void
     {
diff --git a/src/Entities/Activity.php b/src/Entities/Activity.php
index 69699f56670bd73639b05f6d8f3cf430e6d1521e..7371efd47f37c1f736017ec7a2f34260b9d9170a 100644
--- a/src/Entities/Activity.php
+++ b/src/Entities/Activity.php
@@ -11,15 +11,18 @@ class Activity
     protected ServiceProvider $serviceProvider;
     protected User $user;
     protected DateTimeImmutable $happenedAt;
+    protected ?string $clientIpAddress;
 
     public function __construct(
         ServiceProvider $serviceProvider,
         User $user,
-        DateTimeImmutable $happenedAt
+        DateTimeImmutable $happenedAt,
+        ?string $clientIpAddress
     ) {
         $this->serviceProvider = $serviceProvider;
         $this->user = $user;
         $this->happenedAt = $happenedAt;
+        $this->clientIpAddress = $clientIpAddress;
     }
 
     /**
@@ -45,4 +48,12 @@ class Activity
     {
         return $this->happenedAt;
     }
+
+    /**
+     * @return string|null
+     */
+    public function getClientIpAddress(): ?string
+    {
+        return $this->clientIpAddress;
+    }
 }
diff --git a/src/Entities/Authentication/State.php b/src/Entities/Authentication/State.php
index 6f0ff682e9169db82241ae27a838b52186af1987..899b3e75d51c500111e729ca8d73293fee7b7a1a 100644
--- a/src/Entities/Authentication/State.php
+++ b/src/Entities/Authentication/State.php
@@ -7,6 +7,7 @@ namespace SimpleSAML\Module\accounting\Entities\Authentication;
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\Helpers\NetworkHelper;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 
 class State
 {
@@ -28,10 +29,15 @@ class State
     protected array $identityProviderMetadata;
     protected array $serviceProviderMetadata;
     protected ?string $clientIpAddress;
+    protected HelpersManager $helpersManager;
 
-    public function __construct(array $state, \DateTimeImmutable $createdAt = null)
-    {
+    public function __construct(
+        array $state,
+        \DateTimeImmutable $createdAt = null,
+        HelpersManager $helpersManager = null
+    ) {
         $this->createdAt = $createdAt ?? new \DateTimeImmutable();
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
 
         $this->identityProviderMetadata = $this->resolveIdentityProviderMetadata($state);
         $this->identityProviderEntityId = $this->resolveIdentityProviderEntityId();
@@ -175,7 +181,7 @@ class State
 
     protected function resolveClientIpAddress(array $state): ?string
     {
-        return NetworkHelper::resolveClientIpAddress(
+        return $this->helpersManager->getNetworkHelper()->resolveClientIpAddress(
             isset($state[self::KEY_ACCOUNTING][self::ACCOUNTING_KEY_CLIENT_IP_ADDRESS]) ?
                 (string)$state[self::KEY_ACCOUNTING][self::ACCOUNTING_KEY_CLIENT_IP_ADDRESS]
                 : null
diff --git a/src/Entities/Bases/AbstractProvider.php b/src/Entities/Bases/AbstractProvider.php
index 63b43535bfc0c75de90730f48d0a2597e4de2123..56c0927f39e52c3a1288b6d742aec30747b3e0a1 100644
--- a/src/Entities/Bases/AbstractProvider.php
+++ b/src/Entities/Bases/AbstractProvider.php
@@ -8,6 +8,7 @@ abstract class AbstractProvider
 {
     public const METADATA_KEY_NAME = 'name';
     public const METADATA_KEY_ENTITY_ID = 'entityid';
+    public const METADATA_KEY_DESCRIPTION = 'description';
 
     protected array $metadata;
     protected string $entityId;
@@ -25,16 +26,7 @@ abstract class AbstractProvider
 
     public function getName(string $locale = 'en'): ?string
     {
-        if (
-            isset($this->metadata[self::METADATA_KEY_NAME]) &&
-            is_array($this->metadata[self::METADATA_KEY_NAME]) &&
-            !empty($this->metadata[self::METADATA_KEY_NAME][$locale]) &&
-            is_string($this->metadata[self::METADATA_KEY_NAME][$locale])
-        ) {
-            return (string)$this->metadata[self::METADATA_KEY_NAME][$locale];
-        }
-
-        return null;
+        return $this->resolveOptionallyLocalizedString(self::METADATA_KEY_NAME, $locale);
     }
 
     public function getEntityId(): string
@@ -42,10 +34,9 @@ abstract class AbstractProvider
         return $this->entityId;
     }
 
-    public function getDescription(): ?string
+    public function getDescription(string $locale = 'en'): ?string
     {
-        // TODO mivanci
-        return null;
+        return $this->resolveOptionallyLocalizedString(self::METADATA_KEY_DESCRIPTION, $locale);
     }
 
 
@@ -60,4 +51,26 @@ abstract class AbstractProvider
 
         throw new UnexpectedValueException('Provider entity metadata does not contain entity ID.');
     }
+
+    protected function resolveOptionallyLocalizedString(string $key, string $locale = 'en'): ?string
+    {
+        if (!isset($this->metadata[$key])) {
+            return null;
+        }
+
+        // Check for non-localized version.
+        if (is_string($this->metadata[$key])) {
+            return $this->metadata[$key];
+        }
+
+        if (
+            is_array($this->metadata[$key]) &&
+            !empty($this->metadata[$key][$locale]) &&
+            is_string($this->metadata[$key][$locale])
+        ) {
+            return $this->metadata[$key][$locale];
+        }
+
+        return null;
+    }
 }
diff --git a/src/Helpers/ArrayHelper.php b/src/Helpers/ArrayHelper.php
index 0b57b663bcd3158113578f9a0741b599d70caf16..7aec51f9069bec8f7991b39d8e581ed5aeb646fc 100644
--- a/src/Helpers/ArrayHelper.php
+++ b/src/Helpers/ArrayHelper.php
@@ -4,15 +4,14 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Module\accounting\Helpers;
 
-// TODO mivanci move to HelpersManager
 class ArrayHelper
 {
-    public static function recursivelySortByKey(array &$array): void
+    public function recursivelySortByKey(array &$array): void
     {
         /** @psalm-suppress MixedAssignment */
         foreach ($array as &$value) {
             if (is_array($value)) {
-                self::recursivelySortByKey($value);
+                $this->recursivelySortByKey($value);
             }
         }
 
diff --git a/src/Helpers/AttributesHelper.php b/src/Helpers/AttributesHelper.php
index e3aedf3ca9f7445e77459ca6cbebc95ac5ac3f8d..8fc6806d02e6ddc926c12f2ec8a9594543009289 100644
--- a/src/Helpers/AttributesHelper.php
+++ b/src/Helpers/AttributesHelper.php
@@ -4,8 +4,6 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Module\accounting\Helpers;
 
-// TODO mivanci move to HelpersManager
-
 class AttributesHelper
 {
     /**
diff --git a/src/Helpers/FilesystemHelper.php b/src/Helpers/FilesystemHelper.php
index f82b41c595d3cc2980cee6ceb5303c0947fcc1fb..1ee692215bc2ce4a7f4f3ecb7a03905e870dd4f8 100644
--- a/src/Helpers/FilesystemHelper.php
+++ b/src/Helpers/FilesystemHelper.php
@@ -6,10 +6,9 @@ namespace SimpleSAML\Module\accounting\Helpers;
 
 use SimpleSAML\Module\accounting\Exceptions\InvalidValueException;
 
-// TODO mivanci move to HelpersManager
 class FilesystemHelper
 {
-    public static function getRealPath(string $path): string
+    public function getRealPath(string $path): string
     {
         $realpath = realpath($path);
 
diff --git a/src/Helpers/HashHelper.php b/src/Helpers/HashHelper.php
index 7421125e4bd5aaab937074a0d30d9f9f9c1dcd0e..ea38562f48cc284eed171c01817d0d30893b69da 100644
--- a/src/Helpers/HashHelper.php
+++ b/src/Helpers/HashHelper.php
@@ -4,17 +4,23 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Module\accounting\Helpers;
 
-// TODO mivanci move to HelpersManager
 class HashHelper
 {
-    public static function getSha256(string $data): string
+    protected ArrayHelper $arrayHelper;
+
+    public function __construct(ArrayHelper $arrayHelper)
+    {
+        $this->arrayHelper = $arrayHelper;
+    }
+
+    public function getSha256(string $data): string
     {
         return hash('sha256', $data);
     }
 
-    public static function getSha256ForArray(array $array): string
+    public function getSha256ForArray(array $array): string
     {
-        ArrayHelper::recursivelySortByKey($array);
-        return self::getSha256(serialize($array));
+        $this->arrayHelper->recursivelySortByKey($array);
+        return $this->getSha256(serialize($array));
     }
 }
diff --git a/src/Helpers/InstanceBuilderUsingModuleConfigurationHelper.php b/src/Helpers/InstanceBuilderUsingModuleConfigurationHelper.php
index ffdba87b2026680708a3aca7fa8ddc2917f62c5c..fed30792f71320136230bf25f825919a4c594d75 100644
--- a/src/Helpers/InstanceBuilderUsingModuleConfigurationHelper.php
+++ b/src/Helpers/InstanceBuilderUsingModuleConfigurationHelper.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace SimpleSAML\Module\accounting\Helpers;
 
 use Psr\Log\LoggerInterface;
@@ -9,7 +11,6 @@ use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\Interfaces\BuildableUsingModuleConfigurationInterface;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 
-// TODO mivanci move to HelpersManager
 class InstanceBuilderUsingModuleConfigurationHelper
 {
     /**
@@ -21,7 +22,7 @@ class InstanceBuilderUsingModuleConfigurationHelper
      * @return BuildableUsingModuleConfigurationInterface
      * @throws Exception
      */
-    public static function build(
+    public function build(
         string $class,
         ModuleConfiguration $moduleConfiguration,
         LoggerInterface $logger,
@@ -29,7 +30,7 @@ class InstanceBuilderUsingModuleConfigurationHelper
         string $method = BuildableUsingModuleConfigurationInterface::BUILD_METHOD
     ): BuildableUsingModuleConfigurationInterface {
         try {
-            self::validateClass($class);
+            $this->validateClass($class);
 
             $allArguments = array_merge([$moduleConfiguration, $logger], $additionalArguments);
 
@@ -47,7 +48,7 @@ class InstanceBuilderUsingModuleConfigurationHelper
         return $instance;
     }
 
-    protected static function validateClass(string $class): void
+    protected function validateClass(string $class): void
     {
         if (!is_subclass_of($class, BuildableUsingModuleConfigurationInterface::class)) {
             $message = sprintf(
diff --git a/src/Helpers/NetworkHelper.php b/src/Helpers/NetworkHelper.php
index 7b245491326d1ad570d66041217a5dccb621855c..d7159a40260aa8386a9b69cbed85635a3ae8ddaf 100644
--- a/src/Helpers/NetworkHelper.php
+++ b/src/Helpers/NetworkHelper.php
@@ -4,10 +4,9 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Module\accounting\Helpers;
 
-// TODO mivanci move to HelpersManager
 class NetworkHelper
 {
-    public static function resolveClientIpAddress(string $clientIpAddress = null): ?string
+    public function resolveClientIpAddress(string $clientIpAddress = null): ?string
     {
         /** @var string|null $clientIpAddress */
         $clientIpAddress = $clientIpAddress ??
diff --git a/src/Http/Controllers/Admin/Configuration.php b/src/Http/Controllers/Admin/Configuration.php
index 6c977e30fd87e43831d3396faf9777c7c2d97d4d..3f585d1e1adb006f08f0801d243eca2c981dae07 100644
--- a/src/Http/Controllers/Admin/Configuration.php
+++ b/src/Http/Controllers/Admin/Configuration.php
@@ -62,7 +62,7 @@ class Configuration
             $moduleConfiguration = new ModuleConfiguration();
 
             $defaultDataTrackerAndProvider =
-                (new AuthenticationDataTrackerBuilder($moduleConfiguration, $this->logger))
+                (new AuthenticationDataTrackerBuilder($moduleConfiguration, $this->logger, $this->helpersManager))
                 ->build($moduleConfiguration->getDefaultDataTrackerAndProviderClass());
 
             if ($defaultDataTrackerAndProvider->needsSetup()) {
@@ -77,7 +77,7 @@ class Configuration
                 $moduleConfiguration->getAccountingProcessingType() ===
                 ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS
             ) {
-                $jobsStore = (new JobsStoreBuilder($moduleConfiguration, $this->logger))
+                $jobsStore = (new JobsStoreBuilder($moduleConfiguration, $this->logger, $this->helpersManager))
                     ->build($moduleConfiguration->getJobsStoreClass());
                 if ($jobsStore->needsSetup()) {
                     if ($runSetup) {
diff --git a/src/Http/Controllers/User/Profile.php b/src/Http/Controllers/User/Profile.php
index cbd467238459c4528d5e723ca99c0d5c3daa1b84..9f4d5ecf27a81218edb970f3330059b222a0d20c 100644
--- a/src/Http/Controllers/User/Profile.php
+++ b/src/Http/Controllers/User/Profile.php
@@ -12,6 +12,7 @@ use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\ModuleConfiguration\ConnectionType;
 use SimpleSAML\Module\accounting\Providers\Builders\AuthenticationDataProviderBuilder;
 use SimpleSAML\Module\accounting\Providers\Interfaces\AuthenticationDataProviderInterface;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Session;
 use SimpleSAML\XHTML\Template;
 use Symfony\Component\HttpFoundation\Request;
@@ -29,6 +30,7 @@ class Profile
     protected string $defaultAuthenticationSource;
     protected Simple $authSimple;
     protected AuthenticationDataProviderBuilder $authenticationDataProviderBuilder;
+    protected HelpersManager $helpersManager;
 
     /**
      * @param ModuleConfiguration $moduleConfiguration
@@ -37,6 +39,7 @@ class Profile
      * @param LoggerInterface $logger
      * @param Simple|null $authSimple
      * @param AuthenticationDataProviderBuilder|null $authenticationDataProviderBuilder
+     * @param HelpersManager|null $helpersManager
      */
     public function __construct(
         ModuleConfiguration $moduleConfiguration,
@@ -44,7 +47,8 @@ class Profile
         Session $session,
         LoggerInterface $logger,
         Simple $authSimple = null,
-        AuthenticationDataProviderBuilder $authenticationDataProviderBuilder = null
+        AuthenticationDataProviderBuilder $authenticationDataProviderBuilder = null,
+        HelpersManager $helpersManager = null
     ) {
         $this->moduleConfiguration = $moduleConfiguration;
         $this->sspConfiguration = $sspConfiguration;
@@ -57,6 +61,8 @@ class Profile
         $this->authenticationDataProviderBuilder = $authenticationDataProviderBuilder ??
             new AuthenticationDataProviderBuilder($this->moduleConfiguration, $this->logger);
 
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
+
         // Make sure the end user is authenticated.
         $this->authSimple->requireAuth();
     }
@@ -162,7 +168,7 @@ class Profile
      */
     protected function prepareToNameAttributeMap(): array
     {
-        return AttributesHelper::getMergedAttributeMapForFiles(
+        return $this->helpersManager->getAttributesHelper()->getMergedAttributeMapForFiles(
             $this->sspConfiguration->getBaseDir(),
             AttributesHelper::MAP_FILES_TO_NAME
         );
diff --git a/src/ModuleConfiguration.php b/src/ModuleConfiguration.php
index 6b4e9abe03c9eeae99c9348f622c1be87526207e..1827a1401191fc90bc710ae3e0262940b454a5ab 100644
--- a/src/ModuleConfiguration.php
+++ b/src/ModuleConfiguration.php
@@ -44,11 +44,13 @@ class ModuleConfiguration
     /**
      * @throws Exception
      */
-    public function __construct(string $fileName = null)
+    public function __construct(string $fileName = null, array $overrides = [])
     {
         $fileName = $fileName ?? self::FILE_NAME;
 
-        $this->configuration = Configuration::getConfig($fileName);
+        $fullConfigArray = array_merge(Configuration::getConfig($fileName)->toArray(), $overrides);
+
+        $this->configuration = Configuration::loadFromArray($fullConfigArray);
 
         $this->validate();
     }
diff --git a/src/Providers/Builders/AuthenticationDataProviderBuilder.php b/src/Providers/Builders/AuthenticationDataProviderBuilder.php
index 121bc6125edda122fc716d8a55103f77b180b85b..b064f1c2cd8efd288dfbd7643d2e75b083f951e7 100644
--- a/src/Providers/Builders/AuthenticationDataProviderBuilder.php
+++ b/src/Providers/Builders/AuthenticationDataProviderBuilder.php
@@ -10,17 +10,23 @@ use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfigurationHelper;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Providers\Interfaces\AuthenticationDataProviderInterface;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use Throwable;
 
 class AuthenticationDataProviderBuilder
 {
     protected ModuleConfiguration $moduleConfiguration;
     protected LoggerInterface $logger;
+    protected HelpersManager $helpersManager;
 
-    public function __construct(ModuleConfiguration $moduleConfiguration, LoggerInterface $logger)
-    {
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        HelpersManager $helpersManager
+    ) {
         $this->moduleConfiguration = $moduleConfiguration;
         $this->logger = $logger;
+        $this->helpersManager = $helpersManager;
     }
 
     /**
@@ -43,7 +49,7 @@ class AuthenticationDataProviderBuilder
 
             // Build...
             /** @var AuthenticationDataProviderInterface $store */
-            $store = InstanceBuilderUsingModuleConfigurationHelper::build(
+            $store = $this->helpersManager->getInstanceBuilderUsingModuleConfigurationHelper()->build(
                 $class,
                 $this->moduleConfiguration,
                 $this->logger,
diff --git a/src/Services/HelpersManager.php b/src/Services/HelpersManager.php
index 43b85fecdd5b25355bfa366b115dd1b55022927e..e66518fa691269da596f533a7451013f1878c604 100644
--- a/src/Services/HelpersManager.php
+++ b/src/Services/HelpersManager.php
@@ -4,8 +4,14 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Module\accounting\Services;
 
+use SimpleSAML\Module\accounting\Helpers\ArrayHelper;
+use SimpleSAML\Module\accounting\Helpers\AttributesHelper;
 use SimpleSAML\Module\accounting\Helpers\DateTimeHelper;
 use SimpleSAML\Module\accounting\Helpers\EnvironmentHelper;
+use SimpleSAML\Module\accounting\Helpers\FilesystemHelper;
+use SimpleSAML\Module\accounting\Helpers\HashHelper;
+use SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfigurationHelper;
+use SimpleSAML\Module\accounting\Helpers\NetworkHelper;
 use SimpleSAML\Module\accounting\Helpers\RandomHelper;
 use SimpleSAML\Module\accounting\Helpers\ModuleRoutesHelper;
 
@@ -15,6 +21,12 @@ class HelpersManager
     protected static ?EnvironmentHelper $environmentHelper;
     protected static ?RandomHelper $randomHelper;
     protected static ?ModuleRoutesHelper $routesHelper;
+    protected static ?ArrayHelper $arrayHelper;
+    protected static ?HashHelper $hashHelper;
+    protected static ?AttributesHelper $attributesHelper;
+    protected static ?FilesystemHelper $filesystemHelper;
+    protected static ?InstanceBuilderUsingModuleConfigurationHelper $instanceBuilderHelper;
+    protected static ?NetworkHelper $networkHelper;
 
     public function getDateTimeHelper(): DateTimeHelper
     {
@@ -35,4 +47,34 @@ class HelpersManager
     {
         return self::$routesHelper ??= new ModuleRoutesHelper();
     }
+
+    public function getArrayHelper(): ArrayHelper
+    {
+        return self::$arrayHelper ??= new ArrayHelper();
+    }
+
+    public function getHashHelper(): HashHelper
+    {
+        return self::$hashHelper ??= new HashHelper($this->getArrayHelper());
+    }
+
+    public function getAttributesHelper(): AttributesHelper
+    {
+        return self::$attributesHelper ??= new AttributesHelper();
+    }
+
+    public function getFilesystemHelper(): FilesystemHelper
+    {
+        return self::$filesystemHelper ??= new FilesystemHelper();
+    }
+
+    public function getInstanceBuilderUsingModuleConfigurationHelper(): InstanceBuilderUsingModuleConfigurationHelper
+    {
+        return self::$instanceBuilderHelper ??= new InstanceBuilderUsingModuleConfigurationHelper();
+    }
+
+    public function getNetworkHelper(): NetworkHelper
+    {
+        return self::$networkHelper ??= new NetworkHelper();
+    }
 }
diff --git a/src/Services/JobRunner.php b/src/Services/JobRunner.php
index 9735bfa3ce3c65c124b09265bdec2c60d4f24d3a..eb179489a113a185c12d7f411f0e188e2aa76ada 100644
--- a/src/Services/JobRunner.php
+++ b/src/Services/JobRunner.php
@@ -53,24 +53,25 @@ class JobRunner
         ModuleConfiguration $moduleConfiguration,
         SspConfiguration $sspConfiguration,
         LoggerInterface $logger = null,
+        HelpersManager $helpersManager = null,
         AuthenticationDataTrackerBuilder $authenticationDataTrackerBuilder = null,
         JobsStoreBuilder $jobsStoreBuilder = null,
         CacheInterface $cache = null,
         State $state = null,
-        RateLimiter $rateLimiter = null,
-        HelpersManager $helpersManager = null
+        RateLimiter $rateLimiter = null
     ) {
         $this->moduleConfiguration = $moduleConfiguration;
         $this->sspConfiguration = $sspConfiguration;
         $this->logger = $logger ?? new Logger();
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
+
         $this->authenticationDataTrackerBuilder = $authenticationDataTrackerBuilder ??
-            new AuthenticationDataTrackerBuilder($this->moduleConfiguration, $this->logger);
-        $this->jobsStoreBuilder = $jobsStoreBuilder ?? new JobsStoreBuilder($this->moduleConfiguration, $this->logger);
+            new AuthenticationDataTrackerBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
+        $this->jobsStoreBuilder = $jobsStoreBuilder ??
+            new JobsStoreBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
 
         $this->cache = $cache ?? $this->resolveCache();
 
-        $this->helpersManager = $helpersManager ?? new HelpersManager();
-
         $this->jobRunnerId = $this->helpersManager->getRandomHelper()->getRandomInt();
 
         $this->state = $state ?? new State($this->jobRunnerId);
diff --git a/src/Stores/Builders/Bases/AbstractStoreBuilder.php b/src/Stores/Builders/Bases/AbstractStoreBuilder.php
index 7e96d3fc0d6b1f0191935f098238ea60f6a95d97..5cce33d23c7d4e885ede1e5d9e4af5316e94ec2d 100644
--- a/src/Stores/Builders/Bases/AbstractStoreBuilder.php
+++ b/src/Stores/Builders/Bases/AbstractStoreBuilder.php
@@ -6,8 +6,8 @@ namespace SimpleSAML\Module\accounting\Stores\Builders\Bases;
 
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
-use SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfigurationHelper;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Interfaces\StoreInterface;
 use Throwable;
 
@@ -18,11 +18,16 @@ abstract class AbstractStoreBuilder
 {
     protected ModuleConfiguration $moduleConfiguration;
     protected LoggerInterface $logger;
+    protected HelpersManager $helpersManager;
 
-    public function __construct(ModuleConfiguration $moduleConfiguration, LoggerInterface $logger)
-    {
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        HelpersManager $helpersManager
+    ) {
         $this->moduleConfiguration = $moduleConfiguration;
         $this->logger = $logger;
+        $this->helpersManager = $helpersManager;
     }
 
     abstract public function build(
@@ -44,7 +49,7 @@ abstract class AbstractStoreBuilder
 
             // Build store...
             /** @var StoreInterface $store */
-            $store = InstanceBuilderUsingModuleConfigurationHelper::build(
+            $store = $this->helpersManager->getInstanceBuilderUsingModuleConfigurationHelper()->build(
                 $class,
                 $this->moduleConfiguration,
                 $this->logger,
diff --git a/src/Stores/Connections/Bases/AbstractMigrator.php b/src/Stores/Connections/Bases/AbstractMigrator.php
index 613efc1b6cd5eb47853cbe0471d6b774f54f8ff8..2cdb590c78825979a5895e3dd80a4308e57d7270 100644
--- a/src/Stores/Connections/Bases/AbstractMigrator.php
+++ b/src/Stores/Connections/Bases/AbstractMigrator.php
@@ -7,6 +7,7 @@ namespace SimpleSAML\Module\accounting\Stores\Connections\Bases;
 use SimpleSAML\Module\accounting\Exceptions\InvalidValueException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
 use SimpleSAML\Module\accounting\Helpers\FilesystemHelper;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
 use Throwable;
 
@@ -14,6 +15,13 @@ abstract class AbstractMigrator
 {
     public const DEFAULT_MIGRATIONS_DIRECTORY_NAME = 'Migrations';
 
+    protected HelpersManager $helpersManager;
+
+    public function __construct(HelpersManager $helpersManager = null)
+    {
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
+    }
+
     /**
      * @param string $directory
      * @param string $namespace
@@ -21,7 +29,7 @@ abstract class AbstractMigrator
      */
     public function gatherMigrationClassesFromDirectory(string $directory, string $namespace): array
     {
-        $directory = FilesystemHelper::getRealPath($directory);
+        $directory = $this->helpersManager->getFilesystemHelper()->getRealPath($directory);
 
         // Get files without dot directories
         $files = array_values(array_diff(scandir($directory), ['..', '.']));
diff --git a/src/Stores/Connections/DoctrineDbal/Migrator.php b/src/Stores/Connections/DoctrineDbal/Migrator.php
index 5c7250f2c9b8e80c51c0ee2ddef134acfb8bb742..80464462def1241e26a7e54ccee8f60c323f94d3 100644
--- a/src/Stores/Connections/DoctrineDbal/Migrator.php
+++ b/src/Stores/Connections/DoctrineDbal/Migrator.php
@@ -13,6 +13,7 @@ use ReflectionClass;
 use ReflectionException;
 use SimpleSAML\Module\accounting\Exceptions\InvalidValueException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
@@ -35,8 +36,10 @@ class Migrator extends AbstractMigrator
     /**
      * @throws StoreException
      */
-    public function __construct(Connection $connection, LoggerInterface $logger)
+    public function __construct(Connection $connection, LoggerInterface $logger, HelpersManager $helpersManager = null)
     {
+        parent::__construct($helpersManager);
+
         $this->connection = $connection;
         $this->logger = $logger;
 
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store.php b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store.php
index aecebb226923ceef769a2abac1e38ae5cee2a0f4..8bfaed51b7781c92aad3e04f82d409359a13c0c4 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store.php
+++ b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store.php
@@ -12,8 +12,8 @@ use SimpleSAML\Module\accounting\Entities\ServiceProvider;
 use SimpleSAML\Module\accounting\Entities\User;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\Helpers\HashHelper;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
 use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\HashDecoratedState;
@@ -25,6 +25,7 @@ use SimpleSAML\Module\accounting\Stores\Interfaces\DataStoreInterface;
 class Store extends AbstractStore implements DataStoreInterface
 {
     protected Repository $repository;
+    protected HelpersManager $helpersManager;
 
     /**
      * @throws StoreException
@@ -35,11 +36,13 @@ class Store extends AbstractStore implements DataStoreInterface
         Factory $connectionFactory,
         string $connectionKey = null,
         string $connectionType = ModuleConfiguration\ConnectionType::MASTER,
-        Repository $repository = null
+        Repository $repository = null,
+        HelpersManager $helpersManager = null
     ) {
         parent::__construct($moduleConfiguration, $logger, $connectionFactory, $connectionKey, $connectionType);
 
         $this->repository = $repository ?? new Repository($this->connection, $this->logger);
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
     }
 
     /**
@@ -76,8 +79,11 @@ class Store extends AbstractStore implements DataStoreInterface
         $userVersionId = $this->resolveUserVersionId($userId, $hashDecoratedState);
         $idpSpUserVersionId = $this->resolveIdpSpUserVersionId($idpVersionId, $spVersionId, $userVersionId);
 
-        $happenedAt = $authenticationEvent->getHappenedAt();
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $happenedAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $authenticationEvent->getHappenedAt(),
+            $authenticationEvent->getState()->getClientIpAddress()
+        );
     }
 
     /**
@@ -308,7 +314,7 @@ class Store extends AbstractStore implements DataStoreInterface
             throw new UnexpectedValueException($message);
         }
 
-        $userIdentifierValueHashSha256 = HashHelper::getSha256($userIdentifierValue);
+        $userIdentifierValueHashSha256 = $this->helpersManager->getHashHelper()->getSha256($userIdentifierValue);
 
         // Check if it already exists.
         try {
@@ -596,7 +602,12 @@ class Store extends AbstractStore implements DataStoreInterface
                 $user = new User($rawActivity->getUserAttributes());
 
                 $activityBag->add(
-                    new Activity($serviceProvider, $user, $rawActivity->getHappenedAt())
+                    new Activity(
+                        $serviceProvider,
+                        $user,
+                        $rawActivity->getHappenedAt(),
+                        $rawActivity->getClientIpAddress()
+                    )
                 );
             }
         } catch (\Throwable $exception) {
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedState.php b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedState.php
index f349682236936540bc54a559630753096c227eb4..6811a67f59fcccdd6eac391abf89ad0480de81c5 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedState.php
+++ b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedState.php
@@ -1,32 +1,40 @@
 <?php
 
+declare(strict_types=1);
+
 namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
 
 use SimpleSAML\Module\accounting\Entities\Authentication\State;
-use SimpleSAML\Module\accounting\Helpers\HashHelper;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 
 class HashDecoratedState
 {
     protected State $state;
+    protected HelpersManager $helpersManager;
+
     protected string $identityProviderEntityIdHashSha256;
     protected string $serviceProviderEntityIdHashSha256;
     protected string $identityProviderMetadataArrayHashSha256;
     protected string $serviceProviderMetadataArrayHashSha256;
     protected string $attributesArrayHashSha256;
 
-    public function __construct(State $state)
+    public function __construct(State $state, HelpersManager $helpersManager = null)
     {
         $this->state = $state;
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
 
-        $this->identityProviderEntityIdHashSha256 = HashHelper::getSha256($state->getIdentityProviderEntityId());
-        $this->identityProviderMetadataArrayHashSha256 =
-            HashHelper::getSha256ForArray($state->getIdentityProviderMetadata());
+        $this->identityProviderEntityIdHashSha256 = $this->helpersManager->getHashHelper()
+            ->getSha256($state->getIdentityProviderEntityId());
+        $this->identityProviderMetadataArrayHashSha256 = $this->helpersManager->getHashHelper()
+            ->getSha256ForArray($state->getIdentityProviderMetadata());
 
-        $this->serviceProviderEntityIdHashSha256 = HashHelper::getSha256($state->getServiceProviderEntityId());
-        $this->serviceProviderMetadataArrayHashSha256 =
-            HashHelper::getSha256ForArray($state->getServiceProviderMetadata());
+        $this->serviceProviderEntityIdHashSha256 = $this->helpersManager->getHashHelper()
+            ->getSha256($state->getServiceProviderEntityId());
+        $this->serviceProviderMetadataArrayHashSha256 = $this->helpersManager->getHashHelper()
+            ->getSha256ForArray($state->getServiceProviderMetadata());
 
-        $this->attributesArrayHashSha256 = HashHelper::getSha256ForArray($state->getAttributes());
+        $this->attributesArrayHashSha256 = $this->helpersManager->getHashHelper()
+            ->getSha256ForArray($state->getAttributes());
     }
 
     /**
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php
index d56082275448d94f189dfbb22b81f0b6404c3f9c..87938387f3de5f4a645b1b94e6d97928769802bd 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php
+++ b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php
@@ -36,6 +36,10 @@ class Version20220801000700CreateAuthenticationEventTable extends AbstractMigrat
 
             $table->addColumn('happened_at', Types::DATETIMETZ_IMMUTABLE);
 
+            $table->addColumn('client_ip_address', Types::STRING)
+                ->setLength(TableConstants::COLUMN_IP_ADDRESS_LENGTH)
+                ->setNotnull(false);
+
             $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE);
 
             $table->setPrimaryKey(['id']);
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivity.php b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivity.php
index 8a1f7f60ece229149941f7835aca5c343a354129..17fb9e3528b47bd1e36b5a94cfabeda43328cf55 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivity.php
+++ b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivity.php
@@ -14,6 +14,7 @@ class RawActivity extends AbstractRawEntity
     protected array $serviceProviderMetadata;
     protected array $userAttributes;
     protected DateTimeImmutable $happenedAt;
+    protected ?string $clientIpAddress;
 
     public function __construct(array $rawRow, AbstractPlatform $abstractPlatform)
     {
@@ -30,6 +31,10 @@ class RawActivity extends AbstractRawEntity
         $this->happenedAt = $this->resolveDateTimeImmutable(
             $rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT]
         );
+
+        $this->clientIpAddress = empty($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS]) ?
+            null :
+            (string)$rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS];
     }
 
     /**
@@ -56,6 +61,14 @@ class RawActivity extends AbstractRawEntity
         return $this->userAttributes;
     }
 
+    /**
+     * @return string|null
+     */
+    public function getClientIpAddress(): ?string
+    {
+        return $this->clientIpAddress;
+    }
+
     /**
      * @inheritDoc
      */
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Repository.php b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Repository.php
index 976837ca24ce5d01e3d89f3831ef884d98f6bae8..0c198896eea6dec297fd2b4270c15cc955bf2cb4 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Repository.php
+++ b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Repository.php
@@ -654,6 +654,7 @@ class Repository
     public function insertAuthenticationEvent(
         int $IdpSpUserVersionId,
         \DateTimeImmutable $happenedAt,
+        string $clientIpAddress = null,
         \DateTimeImmutable $createdAt = null
     ): void {
         try {
@@ -668,6 +669,8 @@ class Repository
                             TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID,
                         TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => ':' .
                             TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => ':' .
+                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS,
                         TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => ':' .
                             TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT,
                     ]
@@ -677,6 +680,7 @@ class Repository
                         TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID =>
                             $IdpSpUserVersionId,
                         TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => $happenedAt,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => $clientIpAddress,
                         TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => $createdAt,
                     ],
                     [
@@ -684,6 +688,8 @@ class Repository
                             Types::BIGINT,
                         TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT =>
                             Types::DATETIMETZ_IMMUTABLE,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS =>
+                            Types::STRING,
                         TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT =>
                             Types::DATETIMETZ_IMMUTABLE,
                     ]
@@ -961,6 +967,8 @@ class Repository
                 //'vae.happened_at',
                 TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
                 TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT,
+                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
+                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS,
                 //'vsv.metadata AS sp_metadata',
                 TableConstants::TABLE_ALIAS_SP_VERSION . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA .
                 ' AS ' . TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA,
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/TableConstants.php b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/TableConstants.php
index 30aa9a5a76a8b6114df8a7a97915d716604ee3ce..532a8c7325f7249d70cdfcc5c335db7206089fb7 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/TableConstants.php
+++ b/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/TableConstants.php
@@ -12,6 +12,7 @@ class TableConstants
     // https://stackoverflow.com/questions/24196369/what-to-present-at-saml-entityid-url
     public const COLUMN_ENTITY_ID_LENGTH = 1024;
     public const COLUMN_HASH_SHA265_HEXITS_LENGTH = 64;
+    public const COLUMN_IP_ADDRESS_LENGTH = 45;
 
 
     // Table 'idp'
@@ -82,6 +83,7 @@ class TableConstants
     public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_ID = 'id';
     public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID = 'idp_sp_user_version_id';
     public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT = 'happened_at';
+    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address';
     public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT = 'created_at';
 
     // Entity 'ConnectedOrganization' (service provider) related.
@@ -96,4 +98,5 @@ class TableConstants
     public const ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA = 'sp_metadata';
     public const ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes';
     public const ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT = 'happened_at';
+    public const ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address';
 }
diff --git a/src/Trackers/Authentication/DoctrineDbal/Versioned/Tracker.php b/src/Trackers/Authentication/DoctrineDbal/Versioned/Tracker.php
index 6c70e4c9a4582c9f73b9a9fe0cc4a4b034a87608..e974e9a2243f587339f2f09c586e04646443bdef 100644
--- a/src/Trackers/Authentication/DoctrineDbal/Versioned/Tracker.php
+++ b/src/Trackers/Authentication/DoctrineDbal/Versioned/Tracker.php
@@ -8,9 +8,9 @@ use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Entities\Activity;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
-use SimpleSAML\Module\accounting\Helpers\HashHelper;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Providers\Interfaces\AuthenticationDataProviderInterface;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Builders\DataStoreBuilder;
 use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
 use SimpleSAML\Module\accounting\Stores\Interfaces\DataStoreInterface;
@@ -21,19 +21,23 @@ class Tracker implements AuthenticationDataTrackerInterface, AuthenticationDataP
     protected ModuleConfiguration $moduleConfiguration;
     protected LoggerInterface $logger;
     protected DataStoreInterface $dataStore;
+    protected HelpersManager $helpersManager;
 
     public function __construct(
         ModuleConfiguration $moduleConfiguration,
         LoggerInterface $logger,
         string $connectionType = ModuleConfiguration\ConnectionType::MASTER,
+        HelpersManager $helpersManager = null,
         DataStoreInterface $dataStore = null
     ) {
         $this->moduleConfiguration = $moduleConfiguration;
         $this->logger = $logger;
 
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
+
         // Use provided store or initialize default store for this tracker.
         $this->dataStore = $dataStore ??
-            (new DataStoreBuilder($this->moduleConfiguration, $this->logger))
+            (new DataStoreBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager))
                 ->build(
                     Store::class,
                     $this->moduleConfiguration->getClassConnectionKey(self::class),
@@ -71,13 +75,13 @@ class Tracker implements AuthenticationDataTrackerInterface, AuthenticationDataP
 
     public function getConnectedServiceProviders(string $userIdentifier): ConnectedServiceProvider\Bag
     {
-        $userIdentifierHashSha256 = HashHelper::getSha256($userIdentifier);
+        $userIdentifierHashSha256 = $this->helpersManager->getHashHelper()->getSha256($userIdentifier);
         return $this->dataStore->getConnectedOrganizations($userIdentifierHashSha256);
     }
 
     public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag
     {
-        $userIdentifierHashSha256 = HashHelper::getSha256($userIdentifier);
+        $userIdentifierHashSha256 = $this->helpersManager->getHashHelper()->getSha256($userIdentifier);
         return $this->dataStore->getActivity($userIdentifierHashSha256, $maxResults, $firstResult);
     }
 }
diff --git a/src/Trackers/Builders/AuthenticationDataTrackerBuilder.php b/src/Trackers/Builders/AuthenticationDataTrackerBuilder.php
index e584a6c9f8ed930200ea2a4087f4b00599b922ee..388338d13de848a44b020c25d3d9131ac541738a 100644
--- a/src/Trackers/Builders/AuthenticationDataTrackerBuilder.php
+++ b/src/Trackers/Builders/AuthenticationDataTrackerBuilder.php
@@ -7,6 +7,7 @@ use SimpleSAML\Module\accounting\Exceptions\Exception;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfigurationHelper;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
 use Throwable;
 
@@ -14,11 +15,16 @@ class AuthenticationDataTrackerBuilder
 {
     protected ModuleConfiguration $moduleConfiguration;
     protected LoggerInterface $logger;
+    protected HelpersManager $helpersManager;
 
-    public function __construct(ModuleConfiguration $moduleConfiguration, LoggerInterface $logger)
-    {
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        HelpersManager $helpersManager
+    ) {
         $this->moduleConfiguration = $moduleConfiguration;
         $this->logger = $logger;
+        $this->helpersManager = $helpersManager;
     }
 
     /**
@@ -39,7 +45,7 @@ class AuthenticationDataTrackerBuilder
 
             // Build...
             /** @var AuthenticationDataTrackerInterface $store */
-            $store = InstanceBuilderUsingModuleConfigurationHelper::build(
+            $store = $this->helpersManager->getInstanceBuilderUsingModuleConfigurationHelper()->build(
                 $class,
                 $this->moduleConfiguration,
                 $this->logger
diff --git a/templates/user/activity.twig b/templates/user/activity.twig
index c505f4349b6a2125611e326c1b5dafce104a7db7..5f77015382f7be5ffaa6e3ff22e554ad33ffd11a 100644
--- a/templates/user/activity.twig
+++ b/templates/user/activity.twig
@@ -32,6 +32,9 @@
             </tr>
             <tr class="panel">
                 <td colspan="3">
+                    <strong>{{ 'IP address'|trans }}</strong>
+                    <ul><li>{{ activity.getClientIpAddress }}</li></ul>
+
                     <strong>{{ 'Information transfered to service'|trans }}</strong>
                     <ul>
                         {% for name, value in activity.getUser.getAttributes %}
@@ -42,6 +45,10 @@
                     </ul>
                 </td>
             </tr>
+        {% else %}
+            <tr>
+                <td colspan="3">{{ 'No data available'|trans }}</td>
+            </tr>
         {% endfor %}
         </tbody>
     </table>
diff --git a/templates/user/connected-organizations.twig b/templates/user/connected-organizations.twig
index 0229fc575914a778db484bf43d9b5b3cc70d339f..0d5a57849814c6b3e21e6f52c19dda0250ca6395 100644
--- a/templates/user/connected-organizations.twig
+++ b/templates/user/connected-organizations.twig
@@ -54,6 +54,10 @@
                     </ul>
                 </td>
             </tr>
+        {% else %}
+            <tr>
+                <td colspan="3">{{ 'No data available'|trans }}</td>
+            </tr>
         {% endfor %}
         </tbody>
     </table>
diff --git a/tests/config-templates/invalid_array_value_module_accounting.php b/tests/config-templates/invalid_array_value_module_accounting.php
deleted file mode 100644
index b5f3c8a7bca75dc435b7b801a1e48d989dc0766e..0000000000000000000000000000000000000000
--- a/tests/config-templates/invalid_array_value_module_accounting.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores;
-use SimpleSAML\Module\accounting\Trackers;
-
-$config = [
-    ModuleConfiguration::OPTION_USER_ID_ATTRIBUTE_NAME => 'urn:oasis:names:tc:SAML:attribute:subject-id',
-
-    ModuleConfiguration::OPTION_ACCOUNTING_PROCESSING_TYPE =>
-        ModuleConfiguration\AccountingProcessingType::VALUE_SYNCHRONOUS,
-
-    ModuleConfiguration::OPTION_JOBS_STORE => Stores\Jobs\DoctrineDbal\Store::class,
-
-    ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER =>
-        Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class,
-
-    ModuleConfiguration::OPTION_CLASS_TO_CONNECTION_MAP => [
-        'invalid-array-value' => [
-            'no-master-key' => 'invalid',
-        ],
-    ],
-    ModuleConfiguration::OPTION_ADDITIONAL_TRACKERS => [
-        'invalid',
-    ],
-
-    ModuleConfiguration::OPTION_CONNECTIONS_AND_PARAMETERS => [
-        'doctrine_dbal_pdo_mysql' => [
-            'driver' => 'pdo_mysql',
-        ],
-        'doctrine_dbal_pdo_sqlite' => [
-            'driver' => 'pdo_sqlite',
-        ],
-    ],
-];
diff --git a/tests/config-templates/invalid_async_module_accounting.php b/tests/config-templates/invalid_async_module_accounting.php
deleted file mode 100644
index 77a3517abfce30e0a334f063a1ecea333492a065..0000000000000000000000000000000000000000
--- a/tests/config-templates/invalid_async_module_accounting.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores;
-use SimpleSAML\Module\accounting\Trackers;
-
-$config = [
-    ModuleConfiguration::OPTION_USER_ID_ATTRIBUTE_NAME => 'urn:oasis:names:tc:SAML:attribute:subject-id',
-
-    ModuleConfiguration::OPTION_ACCOUNTING_PROCESSING_TYPE =>
-        ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS,
-
-    ModuleConfiguration::OPTION_JOBS_STORE => 'invalid',
-
-    ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER => 'invalid',
-
-    ModuleConfiguration::OPTION_ADDITIONAL_TRACKERS => [
-        'invalid',
-    ],
-
-    ModuleConfiguration::OPTION_CLASS_TO_CONNECTION_MAP => [
-        'invalid'
-    ],
-
-    ModuleConfiguration::OPTION_CONNECTIONS_AND_PARAMETERS => [
-        'doctrine_dbal_pdo_mysql' => [
-            'driver' => 'pdo_mysql',
-        ],
-        'doctrine_dbal_pdo_sqlite' => [
-            'driver' => 'pdo_sqlite',
-        ],
-    ],
-];
diff --git a/tests/config-templates/invalid_module_accounting.php b/tests/config-templates/invalid_module_accounting.php
deleted file mode 100644
index 5a700dbcb4ea13102ed5a7bd1fe871cfa5109910..0000000000000000000000000000000000000000
--- a/tests/config-templates/invalid_module_accounting.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores;
-use SimpleSAML\Module\accounting\Trackers;
-
-$config = [
-    ModuleConfiguration::OPTION_USER_ID_ATTRIBUTE_NAME => 'urn:oasis:names:tc:SAML:attribute:subject-id',
-
-    ModuleConfiguration::OPTION_ACCOUNTING_PROCESSING_TYPE => 'invalid',
-
-    ModuleConfiguration::OPTION_JOBS_STORE => 'invalid',
-
-    ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER => 'invalid',
-
-    ModuleConfiguration::OPTION_CLASS_TO_CONNECTION_MAP => [
-        /**
-         * Connection key to be used by jobs store class.
-         */
-        Stores\Jobs\DoctrineDbal\Store::class => 'invalid',
-        /**
-         * Connection key to be used by this data tracker and provider.
-         */
-        Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class => [
-            ModuleConfiguration\ConnectionType::MASTER => 'invalid',
-            ModuleConfiguration\ConnectionType::SLAVE => [
-                'invalid',
-            ],
-        ],
-    ],
-    ModuleConfiguration::OPTION_ADDITIONAL_TRACKERS => [
-        'invalid',
-        ['invalid']
-    ],
-
-    ModuleConfiguration::OPTION_CONNECTIONS_AND_PARAMETERS => [
-        'doctrine_dbal_pdo_mysql' => [
-            'driver' => 'pdo_mysql',
-        ],
-        'doctrine_dbal_pdo_sqlite' => [
-            'driver' => 'pdo_sqlite',
-        ],
-    ],
-];
diff --git a/tests/config-templates/invalid_object_value_module_accounting.php b/tests/config-templates/invalid_object_value_module_accounting.php
deleted file mode 100644
index 6c46610918610bac25f3adc7507da16c88cf84dc..0000000000000000000000000000000000000000
--- a/tests/config-templates/invalid_object_value_module_accounting.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores;
-use SimpleSAML\Module\accounting\Trackers;
-
-$config = [
-    ModuleConfiguration::OPTION_USER_ID_ATTRIBUTE_NAME => 'urn:oasis:names:tc:SAML:attribute:subject-id',
-
-    ModuleConfiguration::OPTION_ACCOUNTING_PROCESSING_TYPE =>
-        ModuleConfiguration\AccountingProcessingType::VALUE_SYNCHRONOUS,
-
-    ModuleConfiguration::OPTION_JOBS_STORE => Stores\Jobs\DoctrineDbal\Store::class,
-
-    ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER =>
-        Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class,
-
-    ModuleConfiguration::OPTION_CLASS_TO_CONNECTION_MAP => [
-        'invalid-object-value' => new stdClass(),
-    ],
-    ModuleConfiguration::OPTION_ADDITIONAL_TRACKERS => [
-        'invalid',
-    ],
-
-    ModuleConfiguration::OPTION_CONNECTIONS_AND_PARAMETERS => [
-        'doctrine_dbal_pdo_mysql' => [
-            'driver' => 'pdo_mysql',
-        ],
-        'doctrine_dbal_pdo_sqlite' => [
-            'driver' => 'pdo_sqlite',
-        ],
-    ],
-];
diff --git a/tests/config-templates/module_accounting.php b/tests/config-templates/module_accounting.php
index 60da309e0f79caa532a4c0f0b9d16d4972d62ad2..82daf2b42edd594c4368cf2efeb6c4019d02ff5d 100644
--- a/tests/config-templates/module_accounting.php
+++ b/tests/config-templates/module_accounting.php
@@ -49,4 +49,8 @@ $config = [
             'table_prefix' => '',
         ],
     ],
+
+    ModuleConfiguration::OPTION_JOB_RUNNER_MAXIMUM_EXECUTION_TIME => null,
+
+    ModuleConfiguration::OPTION_JOB_RUNNER_SHOULD_PAUSE_AFTER_NUMBER_OF_JOBS_PROCESSED => 10,
 ];
diff --git a/tests/src/Auth/Process/AccountingTest.php b/tests/src/Auth/Process/AccountingTest.php
index c00dbfdc7478efe66a40a72001ba8bb845cef0fa..7e932e0856e83dfc5c9c624dbbda1d90df3ebb61 100644
--- a/tests/src/Auth/Process/AccountingTest.php
+++ b/tests/src/Auth/Process/AccountingTest.php
@@ -11,6 +11,7 @@ use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Entities\Authentication\State;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
@@ -31,6 +32,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @uses   \SimpleSAML\Module\accounting\Entities\Authentication\Event\Job
  * @uses   \SimpleSAML\Module\accounting\Entities\Bases\AbstractJob
  * @uses   \SimpleSAML\Module\accounting\Helpers\NetworkHelper
+ * @uses   \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class AccountingTest extends TestCase
 {
@@ -42,6 +44,27 @@ class AccountingTest extends TestCase
     protected \PHPUnit\Framework\MockObject\MockObject $jobsStoreMock;
     protected \PHPUnit\Framework\MockObject\MockObject $trackerMock;
     protected array $sampleState;
+    protected HelpersManager $helpersManager;
+
+    protected function setUp(): void
+    {
+        $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
+
+        $this->loggerMock = $this->createMock(LoggerInterface::class);
+
+        $this->jobsStoreBuilderMock = $this->createMock(JobsStoreBuilder::class);
+        $this->authenticationDataTrackerBuilderMock =
+            $this->createMock(AuthenticationDataTrackerBuilder::class);
+
+        $this->jobsStoreMock = $this->createMock(Store::class);
+        $this->trackerMock = $this->createMock(Tracker::class);
+
+        $this->sampleState = StateArrays::FULL;
+
+        $this->filterConfig = [];
+
+        $this->helpersManager = new HelpersManager();
+    }
 
     public function testCanCreateInstance(): void
     {
@@ -64,6 +87,7 @@ class AccountingTest extends TestCase
                 null,
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
+                $this->helpersManager,
                 $this->jobsStoreBuilderMock,
                 $this->authenticationDataTrackerBuilderMock
             )
@@ -96,6 +120,7 @@ class AccountingTest extends TestCase
             null,
             $this->moduleConfigurationStub,
             $this->loggerMock,
+            $this->helpersManager,
             $this->jobsStoreBuilderMock,
             $this->authenticationDataTrackerBuilderMock
         ))->process($this->sampleState);
@@ -130,6 +155,7 @@ class AccountingTest extends TestCase
             null,
             $this->moduleConfigurationStub,
             $this->loggerMock,
+            $this->helpersManager,
             $this->jobsStoreBuilderMock,
             $this->authenticationDataTrackerBuilderMock
         ))->process($this->sampleState);
@@ -148,26 +174,9 @@ class AccountingTest extends TestCase
             null,
             $this->moduleConfigurationStub,
             $this->loggerMock,
+            $this->helpersManager,
             $this->jobsStoreBuilderMock,
             $this->authenticationDataTrackerBuilderMock
         ))->process($this->sampleState);
     }
-
-    protected function setUp(): void
-    {
-        $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
-
-        $this->loggerMock = $this->createMock(LoggerInterface::class);
-
-        $this->jobsStoreBuilderMock = $this->createMock(JobsStoreBuilder::class);
-        $this->authenticationDataTrackerBuilderMock =
-            $this->createMock(AuthenticationDataTrackerBuilder::class);
-
-        $this->jobsStoreMock = $this->createMock(Store::class);
-        $this->trackerMock = $this->createMock(Tracker::class);
-
-        $this->sampleState = StateArrays::FULL;
-
-        $this->filterConfig = [];
-    }
 }
diff --git a/tests/src/Constants/RawRowResult.php b/tests/src/Constants/RawRowResult.php
index f09e6fc597070ad9b05401e60fce97c1d276979d..e4224dfa1814ffbab19be377f82b74f1831d4abc 100644
--- a/tests/src/Constants/RawRowResult.php
+++ b/tests/src/Constants/RawRowResult.php
@@ -22,5 +22,6 @@ class RawRowResult
         TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT => '2022-02-22 22:22:22',
         TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA => 'a:9:{s:19:"SingleLogoutService";a:1:{i:0;a:2:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect";s:8:"Location";s:121:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/singleLogoutService/default-sp";}}s:24:"AssertionConsumerService";a:2:{i:0;a:3:{s:7:"Binding";s:46:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:0;}i:1;a:3:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:1;}}s:8:"contacts";a:1:{i:0;a:3:{s:12:"emailAddress";s:15:"mivanci@srce.hr";s:9:"givenName";s:15:"Marko Ivančić";s:11:"contactType";s:9:"technical";}}s:4:"name";a:1:{s:2:"en";s:12:"Test service";}s:11:"description";a:1:{s:2:"en";s:27:"Description of test service";}s:16:"OrganizationName";a:1:{s:2:"en";s:17:"Test organization";}s:8:"entityid";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:14:"metadata-index";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:12:"metadata-set";s:16:"saml20-sp-remote";}',
         TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES => 'a:5:{s:33:"urn:oid:0.9.2342.19200300.100.1.1";a:1:{i:0;s:11:"student-uid";}s:32:"urn:oid:1.3.6.1.4.1.5923.1.1.1.1";a:2:{i:0;s:6:"member";i:1;s:7:"student";}s:15:"urn:oid:2.5.4.4";a:1:{i:0;s:10:"student-sn";}s:19:"hrEduPersonUniqueID";a:1:{i:0;s:19:"student@example.org";}s:23:"hrEduPersonPersistentID";a:1:{i:0;s:13:"student123abc";}}',
+        TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS => '172.21.0.1',
     ];
 }
diff --git a/tests/src/Entities/ActivityTest.php b/tests/src/Entities/ActivityTest.php
index 2d647655b03313da163d6b6a60e106b8afcd64b3..2a35bd27f87167858eb63debcb1409ef8db4a3e6 100644
--- a/tests/src/Entities/ActivityTest.php
+++ b/tests/src/Entities/ActivityTest.php
@@ -23,12 +23,14 @@ class ActivityTest extends TestCase
      */
     protected $userStub;
     protected \DateTimeImmutable $happenedAt;
+    protected string $clientIpAddress;
 
     public function setUp(): void
     {
         $this->serviceProviderStub = $this->createStub(ServiceProvider::class);
         $this->userStub = $this->createStub(User::class);
         $this->happenedAt = new \DateTimeImmutable();
+        $this->clientIpAddress = '123.123.123.123';
     }
 
     public function testCanCreateInstance(): void
@@ -37,11 +39,13 @@ class ActivityTest extends TestCase
         $activity = new Activity(
             $this->serviceProviderStub,
             $this->userStub,
-            $this->happenedAt
+            $this->happenedAt,
+            $this->clientIpAddress
         );
 
         $this->assertSame($this->serviceProviderStub, $activity->getServiceProvider());
         $this->assertSame($this->userStub, $activity->getUser());
         $this->assertSame($this->happenedAt, $activity->getHappenedAt());
+        $this->assertSame($this->clientIpAddress, $activity->getClientIpAddress());
     }
 }
diff --git a/tests/src/Entities/Authentication/Event/JobTest.php b/tests/src/Entities/Authentication/Event/JobTest.php
index 24551ef3409badd98cd82f8387a6f3c8bc5b3704..a43e3bf892e0dfa1a2c7baae9df31c7c1d8506aa 100644
--- a/tests/src/Entities/Authentication/Event/JobTest.php
+++ b/tests/src/Entities/Authentication/Event/JobTest.php
@@ -16,6 +16,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\State
  * @uses \SimpleSAML\Module\accounting\Helpers\NetworkHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class JobTest extends TestCase
 {
diff --git a/tests/src/Entities/Authentication/EventTest.php b/tests/src/Entities/Authentication/EventTest.php
index 0141b2d32d967c7271d3f917457d99d5e426c3cf..58b063437fb3847f9b0d66bcf3876aa6c09b358a 100644
--- a/tests/src/Entities/Authentication/EventTest.php
+++ b/tests/src/Entities/Authentication/EventTest.php
@@ -11,6 +11,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @covers \SimpleSAML\Module\accounting\Entities\Authentication\Event
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\State
  * @uses \SimpleSAML\Module\accounting\Helpers\NetworkHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class EventTest extends TestCase
 {
diff --git a/tests/src/Entities/Authentication/StateTest.php b/tests/src/Entities/Authentication/StateTest.php
index 32ac9e230ae03527bd3026cf7993d2cbbf8916bf..5be1a12aba53891e7236d765a45758532996f529 100644
--- a/tests/src/Entities/Authentication/StateTest.php
+++ b/tests/src/Entities/Authentication/StateTest.php
@@ -12,6 +12,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
 /**
  * @covers \SimpleSAML\Module\accounting\Entities\Authentication\State
  * @uses \SimpleSAML\Module\accounting\Helpers\NetworkHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class StateTest extends TestCase
 {
diff --git a/tests/src/Entities/Bases/AbstractProviderTest.php b/tests/src/Entities/Bases/AbstractProviderTest.php
index ea71711d5822d35a8f173c125ac34f5c1928c4fa..f888d2384167a336bd5ff03c8a3b2dd8c12dcb0b 100644
--- a/tests/src/Entities/Bases/AbstractProviderTest.php
+++ b/tests/src/Entities/Bases/AbstractProviderTest.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace SimpleSAML\Test\Module\accounting\Entities\Bases;
 
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider;
@@ -23,7 +25,10 @@ class AbstractProviderTest extends TestCase
         $this->metadata = [
             AbstractProvider::METADATA_KEY_ENTITY_ID => 'http//example.org/idp',
             AbstractProvider::METADATA_KEY_NAME => [
-                'en' => 'Test',
+                'en' => 'Example service',
+            ],
+            AbstractProvider::METADATA_KEY_DESCRIPTION => [
+                'en' => 'Example description'
             ],
         ];
     }
@@ -44,6 +49,30 @@ class AbstractProviderTest extends TestCase
             $this->metadata[AbstractProvider::METADATA_KEY_NAME]['en'],
             $identityProvider->getName()
         );
+        $this->assertSame(
+            $this->metadata[AbstractProvider::METADATA_KEY_DESCRIPTION]['en'],
+            $identityProvider->getDescription()
+        );
+    }
+
+    public function testCanResolveNonLocalizedString(): void
+    {
+        $metadata = $this->metadata;
+        $metadata[AbstractProvider::METADATA_KEY_DESCRIPTION] = 'Non localized description.';
+
+        $identityProvider = new IdentityProvider($metadata);
+
+        $this->assertSame($metadata[AbstractProvider::METADATA_KEY_DESCRIPTION], $identityProvider->getDescription());
+    }
+
+    public function testInvalidLocalizedDataResolvesToNull(): void
+    {
+        $metadata = $this->metadata;
+        $metadata[AbstractProvider::METADATA_KEY_DESCRIPTION] = false;
+
+        $identityProvider = new IdentityProvider($metadata);
+
+        $this->assertNull($identityProvider->getDescription());
     }
 
     public function testReturnsNullIfNameNotAvailable(): void
diff --git a/tests/src/Helpers/ArrayHelperTest.php b/tests/src/Helpers/ArrayHelperTest.php
index 2e2d77912233193e23d6d38d327d948fb93901a1..204f2886889d694fec9808dffafa0d30bfc05a44 100644
--- a/tests/src/Helpers/ArrayHelperTest.php
+++ b/tests/src/Helpers/ArrayHelperTest.php
@@ -24,7 +24,7 @@ class ArrayHelperTest extends TestCase
 
         $this->assertNotSame($unsorted, $sorted);
 
-        ArrayHelper::recursivelySortByKey($unsorted);
+        (new ArrayHelper())->recursivelySortByKey($unsorted);
 
         $this->assertSame($unsorted, $sorted);
     }
diff --git a/tests/src/Helpers/AttributesHelperTest.php b/tests/src/Helpers/AttributesHelperTest.php
index 9d21c87a33ec8bd68ae96e513fcb8af641db0115..8400b1e042144da64a695aba893e89a5a4f65fde 100644
--- a/tests/src/Helpers/AttributesHelperTest.php
+++ b/tests/src/Helpers/AttributesHelperTest.php
@@ -5,10 +5,12 @@ namespace SimpleSAML\Test\Module\accounting\Helpers;
 use SimpleSAML\Module\accounting\Helpers\AttributesHelper;
 use PHPUnit\Framework\TestCase;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 
 /**
  * @covers \SimpleSAML\Module\accounting\Helpers\AttributesHelper
  * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class AttributesHelperTest extends TestCase
 {
@@ -21,6 +23,7 @@ class AttributesHelperTest extends TestCase
      * @var string[]
      */
     protected array $mapFiles;
+    protected HelpersManager $helpersManager;
 
     protected function setUp(): void
     {
@@ -28,11 +31,14 @@ class AttributesHelperTest extends TestCase
             DIRECTORY_SEPARATOR;
 
         $this->mapFiles = ['test.php', 'test2.php'];
+
+        $this->helpersManager = new HelpersManager();
     }
 
     public function testCanLoadAttributeMaps(): void
     {
-        $fullAttributeMap = AttributesHelper::getMergedAttributeMapForFiles($this->sspBaseDir, $this->mapFiles);
+        $fullAttributeMap = $this->helpersManager->getAttributesHelper()
+            ->getMergedAttributeMapForFiles($this->sspBaseDir, $this->mapFiles);
 
         $this->assertArrayHasKey('mobile', $fullAttributeMap);
         $this->assertArrayHasKey('phone', $fullAttributeMap);
@@ -40,7 +46,8 @@ class AttributesHelperTest extends TestCase
 
     public function testIgnoresNonExistentMaps(): void
     {
-        $fullAttributeMap = AttributesHelper::getMergedAttributeMapForFiles($this->sspBaseDir, ['invalid.php']);
+        $fullAttributeMap = $this->helpersManager->getAttributesHelper()
+            ->getMergedAttributeMapForFiles($this->sspBaseDir, ['invalid.php']);
 
         $this->assertEmpty($fullAttributeMap);
     }
diff --git a/tests/src/Helpers/DateTimeHelperTest.php b/tests/src/Helpers/DateTimeHelperTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ad82ee7fc20d4ff429a104f1d4bb097246e68a1f
--- /dev/null
+++ b/tests/src/Helpers/DateTimeHelperTest.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Helpers;
+
+use SimpleSAML\Module\accounting\Helpers\DateTimeHelper;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Helpers\DateTimeHelper
+ */
+class DateTimeHelperTest extends TestCase
+{
+    public function testCanConvertDateIntervalToSeconds(): void
+    {
+        $interval = new \DateInterval('PT10S');
+
+        $this->assertSame(10, (new DateTimeHelper())->convertDateIntervalToSeconds($interval));
+    }
+
+    public function testMinimumIntervalIsOneSecond(): void
+    {
+        $interval = \DateInterval::createFromDateString('-10 seconds'); // Negative interval
+
+        $this->assertSame(1, (new DateTimeHelper())->convertDateIntervalToSeconds($interval));
+    }
+}
diff --git a/tests/src/Helpers/EnvironmentHelperTest.php b/tests/src/Helpers/EnvironmentHelperTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..223ccd1734d7bd28518ea680d17425048fed0db1
--- /dev/null
+++ b/tests/src/Helpers/EnvironmentHelperTest.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Helpers;
+
+use SimpleSAML\Module\accounting\Helpers\EnvironmentHelper;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Helpers\EnvironmentHelper
+ */
+class EnvironmentHelperTest extends TestCase
+{
+   public function testConfirmIsCli(): void
+   {
+        $this->assertTrue((new EnvironmentHelper())->isCli());
+   }
+}
diff --git a/tests/src/Helpers/FilesystemHelperTest.php b/tests/src/Helpers/FilesystemHelperTest.php
index 01e6be75a9d128b9e97dca3811a96ad302c487ef..e6e76ab8221e7cd97cd931f465a2acfb3458cda5 100644
--- a/tests/src/Helpers/FilesystemHelperTest.php
+++ b/tests/src/Helpers/FilesystemHelperTest.php
@@ -15,7 +15,7 @@ class FilesystemHelperTest extends TestCase
     {
         $path = __DIR__ . DIRECTORY_SEPARATOR . '..';
 
-        $realPath = FilesystemHelper::getRealPath($path);
+        $realPath = (new FilesystemHelper())->getRealPath($path);
 
         $this->assertSame(dirname(__DIR__), $realPath);
     }
@@ -26,6 +26,6 @@ class FilesystemHelperTest extends TestCase
 
         $this->expectException(InvalidValueException::class);
 
-        FilesystemHelper::getRealPath($path);
+        (new FilesystemHelper())->getRealPath($path);
     }
 }
diff --git a/tests/src/Helpers/HashHelperTest.php b/tests/src/Helpers/HashHelperTest.php
index 6c2bf4325f014ff34accc1ef2e20bcd004a54629..e0e620a9b35d3cf7e8fb709388281d9370365269 100644
--- a/tests/src/Helpers/HashHelperTest.php
+++ b/tests/src/Helpers/HashHelperTest.php
@@ -4,8 +4,10 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Test\Module\accounting\Helpers;
 
+use SimpleSAML\Module\accounting\Helpers\ArrayHelper;
 use SimpleSAML\Module\accounting\Helpers\HashHelper;
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 
 /**
  * @covers \SimpleSAML\Module\accounting\Helpers\HashHelper
@@ -13,6 +15,8 @@ use PHPUnit\Framework\TestCase;
  */
 class HashHelperTest extends TestCase
 {
+    protected HashHelper $hashHelper;
+
     protected string $data;
     protected string $dataSha256;
     /**
@@ -28,6 +32,8 @@ class HashHelperTest extends TestCase
 
     protected function setUp(): void
     {
+        $this->hashHelper = new HashHelper(new ArrayHelper());
+
         $this->data = 'test';
         $this->dataSha256 = '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08';
         $this->unsortedArrayData = ['b' => [1 => 1, 0 => 0,], 'a' => [1 => 1, 0 => 0,],];
@@ -38,14 +44,20 @@ class HashHelperTest extends TestCase
 
     public function testCanGetSha256ForString(): void
     {
-        $this->assertSame($this->dataSha256, HashHelper::getSha256($this->data));
+        $this->assertSame($this->dataSha256, $this->hashHelper->getSha256($this->data));
     }
 
     public function testCanGetSha256ForArray(): void
     {
         // Arrays are sorted before the hash is calculated, so the value must be the same.
         $this->assertSame($this->unsortedArraySha256, $this->sortedArrayDataSha256);
-        $this->assertSame($this->unsortedArraySha256, HashHelper::getSha256ForArray($this->unsortedArrayData));
-        $this->assertSame($this->sortedArrayDataSha256, HashHelper::getSha256ForArray($this->sortedArrayData));
+        $this->assertSame(
+            $this->unsortedArraySha256,
+            $this->hashHelper->getSha256ForArray($this->unsortedArrayData)
+        );
+        $this->assertSame(
+            $this->sortedArrayDataSha256,
+            $this->hashHelper->getSha256ForArray($this->sortedArrayData)
+        );
     }
 }
diff --git a/tests/src/Helpers/InstanceBuilderUsingModuleConfigurationHelperTest.php b/tests/src/Helpers/InstanceBuilderUsingModuleConfigurationHelperTest.php
index a1caa56d4ba6a56f6601956407ba388536ba14d3..63345edb3e418dadcc07be93790d4355498c8e22 100644
--- a/tests/src/Helpers/InstanceBuilderUsingModuleConfigurationHelperTest.php
+++ b/tests/src/Helpers/InstanceBuilderUsingModuleConfigurationHelperTest.php
@@ -8,6 +8,7 @@ use SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration
 use PHPUnit\Framework\TestCase;
 use SimpleSAML\Module\accounting\Interfaces\BuildableUsingModuleConfigurationInterface;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 
 /**
  * @covers \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfigurationHelper
@@ -17,7 +18,7 @@ class InstanceBuilderUsingModuleConfigurationHelperTest extends TestCase
     protected BuildableUsingModuleConfigurationInterface $stub;
     /** @var class-string */
     protected string $stubClass;
-    protected \PHPUnit\Framework\MockObject\Stub $moduleConfigurationstub;
+    protected \PHPUnit\Framework\MockObject\Stub $moduleConfigurationStub;
     protected \PHPUnit\Framework\MockObject\Stub $loggerStub;
 
     protected function setUp(): void
@@ -33,7 +34,7 @@ class InstanceBuilderUsingModuleConfigurationHelperTest extends TestCase
 
         $this->stubClass = get_class($this->stub);
 
-        $this->moduleConfigurationstub = $this->createStub(ModuleConfiguration::class);
+        $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
         $this->loggerStub = $this->createStub(LoggerInterface::class);
     }
 
@@ -42,9 +43,9 @@ class InstanceBuilderUsingModuleConfigurationHelperTest extends TestCase
         /** @psalm-suppress InvalidArgument */
         $this->assertInstanceOf(
             BuildableUsingModuleConfigurationInterface::class,
-            InstanceBuilderUsingModuleConfigurationHelper::build(
+            (new InstanceBuilderUsingModuleConfigurationHelper())->build(
                 $this->stubClass,
-                $this->moduleConfigurationstub,
+                $this->moduleConfigurationStub,
                 $this->loggerStub
             )
         );
@@ -55,9 +56,9 @@ class InstanceBuilderUsingModuleConfigurationHelperTest extends TestCase
         $this->expectException(Exception::class);
 
         /** @psalm-suppress InvalidArgument */
-        InstanceBuilderUsingModuleConfigurationHelper::build(
+        (new InstanceBuilderUsingModuleConfigurationHelper())->build(
             ModuleConfiguration::class, // Sample class which is not buildable.
-            $this->moduleConfigurationstub,
+            $this->moduleConfigurationStub,
             $this->loggerStub
         );
     }
diff --git a/tests/src/Helpers/ModuleRoutesHelperTest.php b/tests/src/Helpers/ModuleRoutesHelperTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e9f205958c2c41a4a812d3de336c2431d32566a9
--- /dev/null
+++ b/tests/src/Helpers/ModuleRoutesHelperTest.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Helpers;
+
+use SimpleSAML\Module\accounting\Helpers\ModuleRoutesHelper;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Utils\HTTP;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Helpers\ModuleRoutesHelper
+ */
+class ModuleRoutesHelperTest extends TestCase
+{
+    protected const BASE_URL = 'https://example.org/ssp/';
+    /**
+     * @var \PHPUnit\Framework\MockObject\Stub|HTTP
+     */
+    protected $sspHttpUtilsStub;
+    protected string $moduleUrl;
+
+    protected function setUp(): void
+    {
+        $this->sspHttpUtilsStub = $this->createStub(HTTP::class);
+        $this->sspHttpUtilsStub->method('getBaseURL')->willReturn(self::BASE_URL);
+
+        $this->moduleUrl = self::BASE_URL . 'module.php/' . ModuleConfiguration::MODULE_NAME;
+    }
+
+    public function testCanGetModuleUrl(): void
+    {
+        $path = 'sample-path';
+        $moduleUrlWithPath = $this->moduleUrl . '/' . $path;
+
+        $moduleRoutesHelper = new ModuleRoutesHelper($this->sspHttpUtilsStub);
+
+        $this->assertSame($moduleUrlWithPath, $moduleRoutesHelper->getUrl($path));
+    }
+
+    public function testCanCallMethodToAddParamsToModuleUrl(): void
+    {
+        $path = 'sample-path';
+        $params = ['sample' => 'param'];
+        $fullUrl = 'full-url-with-sample-param';
+
+        $this->sspHttpUtilsStub->method('addURLParameters')->willReturn($fullUrl);
+        $moduleRoutesHelper = new ModuleRoutesHelper($this->sspHttpUtilsStub);
+
+        $this->assertSame($fullUrl, $moduleRoutesHelper->getUrl($path, $params));
+    }
+}
diff --git a/tests/src/Helpers/NetworkHelperTest.php b/tests/src/Helpers/NetworkHelperTest.php
index d4c9abe03a73b2e5649275dc2142c684d5332fc4..98cfed24aa0f38753101dcc71ae7f9438eda739e 100644
--- a/tests/src/Helpers/NetworkHelperTest.php
+++ b/tests/src/Helpers/NetworkHelperTest.php
@@ -21,17 +21,17 @@ class NetworkHelperTest extends TestCase
 
     public function testCanGetIpFromParameter(): void
     {
-        $this->assertSame($this->ipAddress, NetworkHelper::resolveClientIpAddress($this->ipAddress));
+        $this->assertSame($this->ipAddress, (new NetworkHelper())->resolveClientIpAddress($this->ipAddress));
     }
 
     public function testReturnsNullForInvalidIp(): void
     {
-        $this->assertNull(NetworkHelper::resolveClientIpAddress('invalid'));
+        $this->assertNull((new NetworkHelper())->resolveClientIpAddress('invalid'));
     }
 
     public function testReturnsNullForNonExistentIp(): void
     {
-        $this->assertNull(NetworkHelper::resolveClientIpAddress());
+        $this->assertNull((new NetworkHelper())->resolveClientIpAddress());
     }
 
     /**
@@ -43,6 +43,6 @@ class NetworkHelperTest extends TestCase
 
         $_SERVER['REMOTE_ADDR'] = $this->ipAddress;
 
-        $this->assertSame($this->ipAddress, NetworkHelper::resolveClientIpAddress());
+        $this->assertSame($this->ipAddress, (new NetworkHelper())->resolveClientIpAddress());
     }
 }
diff --git a/tests/src/Helpers/RandomHelperTest.php b/tests/src/Helpers/RandomHelperTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..538b3136906ca88b5744e959d252f5f57e665ab2
--- /dev/null
+++ b/tests/src/Helpers/RandomHelperTest.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace SimpleSAML\Test\Module\accounting\Helpers;
+
+use SimpleSAML\Module\accounting\Helpers\RandomHelper;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Helpers\RandomHelper
+ */
+class RandomHelperTest extends TestCase
+{
+    public function testCanGetRandomInt(): void
+    {
+        $this->assertIsInt((new RandomHelper())->getRandomInt());
+    }
+}
diff --git a/tests/src/ModuleConfigurationTest.php b/tests/src/ModuleConfigurationTest.php
index 80821ec553e3044be13026a758575a92605559ae..db2a3d286e794583904a4fa4272b0649f3432ef9 100644
--- a/tests/src/ModuleConfigurationTest.php
+++ b/tests/src/ModuleConfigurationTest.php
@@ -63,14 +63,26 @@ class ModuleConfigurationTest extends TestCase
     {
         $this->expectException(InvalidConfigurationException::class);
 
-        new ModuleConfiguration('invalid_module_accounting.php');
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_ACCOUNTING_PROCESSING_TYPE => 'invalid',
+            ]
+        );
     }
 
     public function testThrowsForInvalidJobsStore(): void
     {
         $this->expectException(InvalidConfigurationException::class);
 
-        new ModuleConfiguration('invalid_async_module_accounting.php');
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_ACCOUNTING_PROCESSING_TYPE =>
+                    ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS,
+                ModuleConfiguration::OPTION_JOBS_STORE => 'invalid',
+            ]
+        );
     }
 
     public function testProperConnectionKeyIsReturned(): void
@@ -96,14 +108,30 @@ class ModuleConfigurationTest extends TestCase
     {
         $this->expectException(InvalidConfigurationException::class);
 
-        new ModuleConfiguration('invalid_object_value_module_accounting.php');
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_CLASS_TO_CONNECTION_MAP => [
+                    'invalid-object-value' => new \stdClass(),
+                ]
+            ]
+        );
     }
 
     public function testThrowsForNonMasterInArrayConnection(): void
     {
         $this->expectException(InvalidConfigurationException::class);
 
-        new ModuleConfiguration('invalid_array_value_module_accounting.php');
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_CLASS_TO_CONNECTION_MAP => [
+                    'invalid-array-value' => [
+                        'no-master-key' => 'invalid',
+                    ],
+                ]
+            ]
+        );
     }
 
     public function testThrowsForInvalidConnectiontype(): void
@@ -157,4 +185,143 @@ class ModuleConfigurationTest extends TestCase
             $this->moduleConfiguration->getModuleRootDirectory()
         );
     }
+
+    public function testCanGetCronTagForJobRunner(): void
+    {
+        $this->assertSame(
+            'accounting_job_runner',
+            $this->moduleConfiguration->getCronTagForJobRunner()
+        );
+    }
+
+    public function testCanGetJobRunnerMaximumExecutionTime(): void
+    {
+        $this->assertNull($this->moduleConfiguration->getJobRunnerMaximumExecutionTime());
+    }
+
+    public function testThrowsForNonStringJobRunnerMaximumExecutionTime(): void
+    {
+        $moduleConfiguration = new ModuleConfiguration(
+            null,
+            [ModuleConfiguration::OPTION_JOB_RUNNER_MAXIMUM_EXECUTION_TIME => false]
+        );
+
+        $this->expectException(InvalidConfigurationException::class);
+
+        $moduleConfiguration->getJobRunnerMaximumExecutionTime();
+    }
+
+    public function testThrowsForInvalidStringJobRunnerMaximumExecutionTime(): void
+    {
+        $moduleConfiguration = new ModuleConfiguration(
+            null,
+            [ModuleConfiguration::OPTION_JOB_RUNNER_MAXIMUM_EXECUTION_TIME => 'invalid']
+        );
+
+
+        $this->expectException(InvalidConfigurationException::class);
+
+        $moduleConfiguration->getJobRunnerMaximumExecutionTime();
+    }
+
+    public function testCanGetJobRunnerShouldPauseAfterNumberOfJobsProcessed(): void
+    {
+        $this->assertSame(10, $this->moduleConfiguration->getJobRunnerShouldPauseAfterNumberOfJobsProcessed());
+    }
+
+    public function testCanGetNullForJobRunnerShouldPauseAfterNumberOfJobsProcessed(): void
+    {
+        $moduleConfiguration = new ModuleConfiguration(
+            null,
+            [ModuleConfiguration::OPTION_JOB_RUNNER_SHOULD_PAUSE_AFTER_NUMBER_OF_JOBS_PROCESSED => null]
+        );
+
+        $this->assertNull($moduleConfiguration->getJobRunnerShouldPauseAfterNumberOfJobsProcessed());
+    }
+
+    public function testThrowsForNonIntegerJobRunnerShouldPauseAfterNumberOfJobsProcessed(): void
+    {
+        $moduleConfiguration = new ModuleConfiguration(
+            null,
+            [ModuleConfiguration::OPTION_JOB_RUNNER_SHOULD_PAUSE_AFTER_NUMBER_OF_JOBS_PROCESSED => false]
+        );
+
+        $this->expectException(InvalidConfigurationException::class);
+
+        $moduleConfiguration->getJobRunnerShouldPauseAfterNumberOfJobsProcessed();
+    }
+
+    public function testThrowsForNegativeIntegerJobRunnerShouldPauseAfterNumberOfJobsProcessed(): void
+    {
+        $moduleConfiguration = new ModuleConfiguration(
+            null,
+            [ModuleConfiguration::OPTION_JOB_RUNNER_SHOULD_PAUSE_AFTER_NUMBER_OF_JOBS_PROCESSED => -1]
+        );
+
+        $this->expectException(InvalidConfigurationException::class);
+
+        $moduleConfiguration->getJobRunnerShouldPauseAfterNumberOfJobsProcessed();
+    }
+
+    public function testThrowsOnInvalidCronTag(): void
+    {
+        $this->expectException(InvalidConfigurationException::class);
+
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_ACCOUNTING_PROCESSING_TYPE =>
+                    ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS,
+                ModuleConfiguration::OPTION_CRON_TAG_FOR_JOB_RUNNER => -1
+            ]
+        );
+    }
+
+    public function testThrowsOnInvalidDefaultDataTrackerAndProvider(): void
+    {
+        $this->expectException(InvalidConfigurationException::class);
+
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER => 'invalid'
+            ]
+        );
+    }
+
+    public function testThrowsOnInvalidAdditionalTrackers(): void
+    {
+        $this->expectException(InvalidConfigurationException::class);
+
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_ADDITIONAL_TRACKERS => ['invalid']
+            ]
+        );
+    }
+
+    public function testThrowsOnNonStringAdditionalTracker(): void
+    {
+        $this->expectException(InvalidConfigurationException::class);
+
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_ADDITIONAL_TRACKERS => [-1]
+            ]
+        );
+    }
+
+    public function testThrowsWhenClassHasNoConnectionParametersSet(): void
+    {
+        $this->expectException(InvalidConfigurationException::class);
+
+        new ModuleConfiguration(
+            null,
+            [
+                ModuleConfiguration::OPTION_CONNECTIONS_AND_PARAMETERS => []
+            ]
+        );
+    }
 }
diff --git a/tests/src/Providers/Builders/AuthenticationDataProviderBuilderTest.php b/tests/src/Providers/Builders/AuthenticationDataProviderBuilderTest.php
index 99c95c87613f4a9e1147195e2e56fed5967f5709..e5c615ce90d5ee9a592ac3f8dd7741f4189ea937 100644
--- a/tests/src/Providers/Builders/AuthenticationDataProviderBuilderTest.php
+++ b/tests/src/Providers/Builders/AuthenticationDataProviderBuilderTest.php
@@ -9,6 +9,7 @@ use SimpleSAML\Module\accounting\Exceptions\Exception;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Providers\Builders\AuthenticationDataProviderBuilder;
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned\Tracker;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
@@ -24,12 +25,15 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
  * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
  * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store
  * @uses \SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned\Tracker
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class AuthenticationDataProviderBuilderTest extends TestCase
 {
     protected \PHPUnit\Framework\MockObject\Stub $moduleConfigurationStub;
 
     protected \PHPUnit\Framework\MockObject\Stub $loggerStub;
+    protected HelpersManager $helpersManager;
 
     protected function setUp(): void
     {
@@ -39,6 +43,7 @@ class AuthenticationDataProviderBuilderTest extends TestCase
             ->willReturn($connectionParams);
 
         $this->loggerStub = $this->createStub(LoggerInterface::class);
+        $this->helpersManager = new HelpersManager();
     }
 
     public function testCanCreateInstance(): void
@@ -46,14 +51,22 @@ class AuthenticationDataProviderBuilderTest extends TestCase
         /** @psalm-suppress InvalidArgument */
         $this->assertInstanceOf(
             AuthenticationDataProviderBuilder::class,
-            new AuthenticationDataProviderBuilder($this->moduleConfigurationStub, $this->loggerStub)
+            new AuthenticationDataProviderBuilder(
+                $this->moduleConfigurationStub,
+                $this->loggerStub,
+                $this->helpersManager
+            )
         );
     }
 
     public function testCanBuildDataProvider(): void
     {
         /** @psalm-suppress InvalidArgument */
-        $builder = new AuthenticationDataProviderBuilder($this->moduleConfigurationStub, $this->loggerStub);
+        $builder = new AuthenticationDataProviderBuilder(
+            $this->moduleConfigurationStub,
+            $this->loggerStub,
+            $this->helpersManager
+        );
 
         $this->assertInstanceOf(Tracker::class, $builder->build(Tracker::class));
     }
@@ -63,7 +76,10 @@ class AuthenticationDataProviderBuilderTest extends TestCase
         $this->expectException(Exception::class);
 
         /** @psalm-suppress InvalidArgument */
-        (new AuthenticationDataProviderBuilder($this->moduleConfigurationStub, $this->loggerStub))
-            ->build('invalid');
+        (new AuthenticationDataProviderBuilder(
+            $this->moduleConfigurationStub,
+            $this->loggerStub,
+            $this->helpersManager
+        ))->build('invalid');
     }
 }
diff --git a/tests/src/Services/HelpersManagerTest.php b/tests/src/Services/HelpersManagerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a199731970637389a04830557f170b4805735799
--- /dev/null
+++ b/tests/src/Services/HelpersManagerTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SimpleSAML\Test\Module\accounting\Services;
+
+use SimpleSAML\Module\accounting\Helpers\ArrayHelper;
+use SimpleSAML\Module\accounting\Helpers\AttributesHelper;
+use SimpleSAML\Module\accounting\Helpers\DateTimeHelper;
+use SimpleSAML\Module\accounting\Helpers\EnvironmentHelper;
+use SimpleSAML\Module\accounting\Helpers\FilesystemHelper;
+use SimpleSAML\Module\accounting\Helpers\HashHelper;
+use SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfigurationHelper;
+use SimpleSAML\Module\accounting\Helpers\ModuleRoutesHelper;
+use SimpleSAML\Module\accounting\Helpers\NetworkHelper;
+use SimpleSAML\Module\accounting\Helpers\RandomHelper;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Services\HelpersManager
+ * @uses \SimpleSAML\Module\accounting\Helpers\ModuleRoutesHelper
+ * @uses \SimpleSAML\Module\accounting\Helpers\HashHelper
+ */
+class HelpersManagerTest extends TestCase
+{
+    public function testCanGetHelperInstances(): void
+    {
+        $helpersManager = new HelpersManager();
+
+        $this->assertInstanceOf(ArrayHelper::class, $helpersManager->getArrayHelper());
+        $this->assertInstanceOf(AttributesHelper::class, $helpersManager->getAttributesHelper());
+        $this->assertInstanceOf(DateTimeHelper::class, $helpersManager->getDateTimeHelper());
+        $this->assertInstanceOf(EnvironmentHelper::class, $helpersManager->getEnvironmentHelper());
+        $this->assertInstanceOf(FilesystemHelper::class, $helpersManager->getFilesystemHelper());
+        $this->assertInstanceOf(HashHelper::class, $helpersManager->getHashHelper());
+        $this->assertInstanceOf(
+            InstanceBuilderUsingModuleConfigurationHelper::class,
+            $helpersManager->getInstanceBuilderUsingModuleConfigurationHelper()
+        );
+        $this->assertInstanceOf(NetworkHelper::class, $helpersManager->getNetworkHelper());
+        $this->assertInstanceOf(RandomHelper::class, $helpersManager->getRandomHelper());
+        $this->assertInstanceOf(ModuleRoutesHelper::class, $helpersManager->getModuleRoutesHelper());
+    }
+}
diff --git a/tests/src/Services/JobRunner/StateTest.php b/tests/src/Services/JobRunner/StateTest.php
index 061b40b4d992fd0107aa8440a62ecbcdf4ed226e..533030e6ec26da4c65dbfdc67239401e21b815fd 100644
--- a/tests/src/Services/JobRunner/StateTest.php
+++ b/tests/src/Services/JobRunner/StateTest.php
@@ -105,4 +105,13 @@ class StateTest extends TestCase
         $this->assertSame(2, count($state->getStatusMessages()));
         $this->assertSame('test3', $state->getLastStatusMessage());
     }
+
+    public function testCanSetGracefulInterruptInitiatedFlag(): void
+    {
+        $state = new State($this->jobRunnerId);
+
+        $this->assertFalse($state->getIsGracefulInterruptInitiated());
+        $state->setIsGracefulInterruptInitiated(true);
+        $this->assertTrue($state->getIsGracefulInterruptInitiated());
+    }
 }
diff --git a/tests/src/Services/JobRunnerTest.php b/tests/src/Services/JobRunnerTest.php
index 8e1fbc94c3f44e15c7fb662a87b064e83d0b6d9b..ceddecc0c39a2ba53366c228814bb14116268c2a 100644
--- a/tests/src/Services/JobRunnerTest.php
+++ b/tests/src/Services/JobRunnerTest.php
@@ -128,12 +128,12 @@ class JobRunnerTest extends TestCase
                 $this->moduleConfigurationStub,
                 $this->sspConfigurationStub,
                 $this->loggerMock,
+                $this->helpersManagerStub,
                 $this->authenticationDataTrackerBuilderStub,
                 $this->jobsStoreBuilderStub,
                 $this->cacheMock,
                 $this->stateStub,
-                $this->rateLimiterMock,
-                $this->helpersManagerStub
+                $this->rateLimiterMock
             )
         );
     }
@@ -160,12 +160,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -193,12 +193,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -222,12 +222,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -256,12 +256,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -293,12 +293,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -325,12 +325,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -363,12 +363,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -401,12 +401,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -446,12 +446,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -481,12 +481,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -518,12 +518,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -555,12 +555,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -594,12 +594,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -634,12 +634,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -675,12 +675,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -726,12 +726,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -774,12 +774,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -838,12 +838,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -896,12 +896,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -965,12 +965,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -1028,12 +1028,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
@@ -1072,12 +1072,12 @@ class JobRunnerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
+            $this->helpersManagerStub,
             $this->authenticationDataTrackerBuilderStub,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
-            $this->rateLimiterMock,
-            $this->helpersManagerStub
+            $this->rateLimiterMock
         );
 
         $jobRunner->run();
diff --git a/tests/src/Stores/Builders/DataStoreBuilderTest.php b/tests/src/Stores/Builders/DataStoreBuilderTest.php
index 30b4214b86d8a0425948a2370580e1dd551d7fa1..3bcc50ab13e2649bdee5bf3e9f305616cf88ec07 100644
--- a/tests/src/Stores/Builders/DataStoreBuilderTest.php
+++ b/tests/src/Stores/Builders/DataStoreBuilderTest.php
@@ -5,6 +5,7 @@ namespace SimpleSAML\Test\Module\accounting\Stores\Builders;
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Builders\DataStoreBuilder;
 use PHPUnit\Framework\TestCase;
 use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
@@ -20,12 +21,15 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
  * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
  * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
  * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class DataStoreBuilderTest extends TestCase
 {
     protected \PHPUnit\Framework\MockObject\Stub $moduleConfigurationStub;
     protected \PHPUnit\Framework\MockObject\Stub $loggerStub;
     protected DataStoreBuilder $dataStoreBuilder;
+    protected HelpersManager $helpersManager;
 
     protected function setUp(): void
     {
@@ -35,8 +39,14 @@ class DataStoreBuilderTest extends TestCase
 
         $this->loggerStub = $this->createStub(LoggerInterface::class);
 
+        $this->helpersManager = new HelpersManager();
+
         /** @psalm-suppress InvalidArgument */
-        $this->dataStoreBuilder = new DataStoreBuilder($this->moduleConfigurationStub, $this->loggerStub);
+        $this->dataStoreBuilder = new DataStoreBuilder(
+            $this->moduleConfigurationStub,
+            $this->loggerStub,
+            $this->helpersManager
+        );
     }
 
     public function testCanBuildDataStore(): void
diff --git a/tests/src/Stores/Builders/JobsStoreBuilderTest.php b/tests/src/Stores/Builders/JobsStoreBuilderTest.php
index 9b954a2248b22984f39fb59a4ea46de3c8042598..906f3457f3db9729775deeea43e2b3756a343b58 100644
--- a/tests/src/Stores/Builders/JobsStoreBuilderTest.php
+++ b/tests/src/Stores/Builders/JobsStoreBuilderTest.php
@@ -1,11 +1,14 @@
 <?php
 
+declare(strict_types=1);
+
 namespace SimpleSAML\Test\Module\accounting\Stores\Builders;
 
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Builders\Bases\AbstractStoreBuilder;
 use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
 use SimpleSAML\Module\accounting\Stores\Interfaces\StoreInterface;
@@ -22,12 +25,15 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
  * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Repository
  * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore
  * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfigurationHelper
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class JobsStoreBuilderTest extends TestCase
 {
     protected \PHPUnit\Framework\MockObject\Stub $moduleConfigurationStub;
     protected \PHPUnit\Framework\MockObject\Stub $loggerStub;
     protected JobsStoreBuilder $jobsStoreBuilder;
+    protected HelpersManager $helpersManager;
 
     protected function setUp(): void
     {
@@ -38,8 +44,14 @@ class JobsStoreBuilderTest extends TestCase
 
         $this->loggerStub = $this->createStub(LoggerInterface::class);
 
+        $this->helpersManager = new HelpersManager();
+
         /** @psalm-suppress InvalidArgument */
-        $this->jobsStoreBuilder = new JobsStoreBuilder($this->moduleConfigurationStub, $this->loggerStub);
+        $this->jobsStoreBuilder = new JobsStoreBuilder(
+            $this->moduleConfigurationStub,
+            $this->loggerStub,
+            $this->helpersManager
+        );
     }
 
     public function testCanBuildJobsStore(): void
@@ -57,7 +69,11 @@ class JobsStoreBuilderTest extends TestCase
         };
 
         /** @psalm-suppress InvalidArgument */
-        $storeBuilder = new class ($moduleConfigurationStub, $this->loggerStub) extends AbstractStoreBuilder {
+        $storeBuilder = new class (
+            $moduleConfigurationStub,
+            $this->loggerStub,
+            $this->helpersManager
+        ) extends AbstractStoreBuilder {
             public function build(
                 string $class,
                 string $connectionKey = null,
@@ -82,7 +98,8 @@ class JobsStoreBuilderTest extends TestCase
         $this->expectException(StoreException::class);
 
         /** @psalm-suppress InvalidArgument */
-        (new JobsStoreBuilder($moduleConfigurationStub, $this->loggerStub))->build('invalid');
+        (new JobsStoreBuilder($moduleConfigurationStub, $this->loggerStub, $this->helpersManager))
+            ->build('invalid');
     }
 
     public function testJobsStoreBuilderOnlyReturnsJobsStores(): void
@@ -114,6 +131,7 @@ class JobsStoreBuilderTest extends TestCase
         $this->expectException(StoreException::class);
 
         /** @psalm-suppress InvalidArgument */
-        (new JobsStoreBuilder($moduleConfigurationStub, $this->loggerStub))->build(get_class($sampleStore));
+        (new JobsStoreBuilder($moduleConfigurationStub, $this->loggerStub, $this->helpersManager))
+            ->build(get_class($sampleStore));
     }
 }
diff --git a/tests/src/Stores/Connections/Bases/AbstractMigratorTest.php b/tests/src/Stores/Connections/Bases/AbstractMigratorTest.php
index 1f4f94dd1b7f023d4f941b7fc096aafc8854dc33..0fb3fda9f697c817da1f36fe9d763f1fc89f091e 100644
--- a/tests/src/Stores/Connections/Bases/AbstractMigratorTest.php
+++ b/tests/src/Stores/Connections/Bases/AbstractMigratorTest.php
@@ -26,6 +26,7 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
  * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
  * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
  * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class AbstractMigratorTest extends TestCase
 {
diff --git a/tests/src/Stores/Connections/DoctrineDbal/FactoryTest.php b/tests/src/Stores/Connections/DoctrineDbal/FactoryTest.php
index 2647f6962160cf7eb0a4399235e7adbba8fea699..07dab2dbfcd72e2a5b750e9edec9b0a581d9c290 100644
--- a/tests/src/Stores/Connections/DoctrineDbal/FactoryTest.php
+++ b/tests/src/Stores/Connections/DoctrineDbal/FactoryTest.php
@@ -15,6 +15,8 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
  * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
  * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
  * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class FactoryTest extends TestCase
 {
diff --git a/tests/src/Stores/Connections/DoctrineDbal/MigratorTest.php b/tests/src/Stores/Connections/DoctrineDbal/MigratorTest.php
index 008d3f139288ea393b8d86b5f1e192bc831dcf73..907e83c6b66d26e1865453145d63b11e1dc02729 100644
--- a/tests/src/Stores/Connections/DoctrineDbal/MigratorTest.php
+++ b/tests/src/Stores/Connections/DoctrineDbal/MigratorTest.php
@@ -28,6 +28,7 @@ use function PHPUnit\Framework\assertFalse;
  * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
  * @uses \SimpleSAML\Module\accounting\Helpers\FilesystemHelper
  * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class MigratorTest extends TestCase
 {
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedStateTest.php b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedStateTest.php
index 0094b57489467a56c49a3195bd1b3b5833364fd4..23cb877c07e19c1100077af13a76191ae585fdd0 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedStateTest.php
+++ b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedStateTest.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
 
 use SimpleSAML\Module\accounting\Entities\Authentication\State;
@@ -10,6 +12,7 @@ use PHPUnit\Framework\TestCase;
  * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\HashDecoratedState
  * @uses \SimpleSAML\Module\accounting\Helpers\HashHelper
  * @uses \SimpleSAML\Module\accounting\Helpers\ArrayHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class HashDecoratedStateTest extends TestCase
 {
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivityTest.php b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivityTest.php
index 2999244c0ddd8e302190767482b1aba675f7c972..d0635ea6f734ed33db25797dc512044f70d96833 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivityTest.php
+++ b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivityTest.php
@@ -24,6 +24,8 @@ class RawActivityTest extends TestCase
      */
     protected array $userAttributes;
     protected string $happenedAt;
+    protected string $clientIpAddress;
+
     protected array $rawRow;
     /**
      * @var AbstractPlatform|AbstractPlatform&\PHPUnit\Framework\MockObject\Stub|\PHPUnit\Framework\MockObject\Stub
@@ -35,10 +37,13 @@ class RawActivityTest extends TestCase
         $this->serviceProviderMetadata = ['sp' => 'metadata'];
         $this->userAttributes = ['user' => 'attribute'];
         $this->happenedAt = '2022-02-22 22:22:22';
+        $this->clientIpAddress = '123.123.123.123';
+
         $this->rawRow = [
             TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA => serialize($this->serviceProviderMetadata),
             TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES => serialize($this->userAttributes),
             TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT => $this->happenedAt,
+            TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS => $this->clientIpAddress,
         ];
         $this->abstractPlatformStub = $this->createStub(AbstractPlatform::class);
         $this->abstractPlatformStub->method('getDateTimeFormatString')->willReturn(DateTime::DEFAULT_FORMAT);
@@ -60,6 +65,16 @@ class RawActivityTest extends TestCase
         $this->assertInstanceOf(\DateTimeImmutable::class, $rawActivity->getHappenedAt());
         $this->assertSame($this->serviceProviderMetadata, $rawActivity->getServiceProviderMetadata());
         $this->assertSame($this->userAttributes, $rawActivity->getUserAttributes());
+        $this->assertSame($this->clientIpAddress, $rawActivity->getClientIpAddress());
+    }
+
+    public function testIpAddressCanBeMissing(): void
+    {
+        $rawRow = $this->rawRow;
+        unset($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS]);
+
+        $rawActivity = new RawActivity($rawRow, $this->abstractPlatformStub);
+        $this->assertNull($rawActivity->getClientIpAddress());
     }
 
     public function testThrowsIfColumnNotPresent(): void
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RepositoryTest.php b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RepositoryTest.php
index ee2f160a3a3015c99e01bdcd4ae1e7300625ca65..ef1b0e1b96e6f6152dc7aa64b2f9a3b11baa92ac 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RepositoryTest.php
+++ b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RepositoryTest.php
@@ -32,6 +32,7 @@ use SimpleSAML\Test\Module\accounting\Constants\DateTime;
  * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable
  * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000600CreateIdpSpUserVersionTable
  * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateAuthenticationEventTable
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  *
  * @psalm-suppress all
  */
@@ -63,6 +64,7 @@ class RepositoryTest extends TestCase
     protected $connectionStub;
     protected string $spEntityIdHash;
     protected string $spMetadata;
+    protected string $clientIpAddress;
 
     protected function setUp(): void
     {
@@ -107,6 +109,7 @@ class RepositoryTest extends TestCase
         $this->userAttributesHash = 'user-attributes-hash';
 
         $this->createdAt = new \DateTimeImmutable();
+        $this->clientIpAddress = '123.123.123.123';
     }
 
     public function testCanCreateInstance(): void
@@ -426,7 +429,7 @@ class RepositoryTest extends TestCase
 
         $this->assertSame(0, (int)$authenticationEventCounterQueryBuilder->executeQuery()->fetchOne());
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $happenedAt, $createdAt);
+        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $happenedAt, null, $createdAt);
 
         $this->assertSame(1, (int)$authenticationEventCounterQueryBuilder->executeQuery()->fetchOne());
     }
@@ -472,7 +475,12 @@ class RepositoryTest extends TestCase
         $idpSpUserVersionId =
             (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $this->createdAt, $this->createdAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->createdAt
+        );
 
         $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
 
@@ -496,7 +504,12 @@ class RepositoryTest extends TestCase
         $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
         $this->assertCount(1, $resultArray);
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $this->createdAt, $this->createdAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->createdAt
+        );
         $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
         $this->assertCount(1, $resultArray);
         $this->assertSame(
@@ -533,7 +546,12 @@ class RepositoryTest extends TestCase
         $idpSpUserVersionId =
             (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $this->createdAt, $this->createdAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->createdAt
+        );
 
         $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
         $this->assertCount(2, $resultArray);
@@ -567,7 +585,12 @@ class RepositoryTest extends TestCase
         $idpSpUserVersionId =
             (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $this->createdAt, $this->createdAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->createdAt
+        );
         $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
 
         $this->assertCount(2, $resultArray);
@@ -637,16 +660,31 @@ class RepositoryTest extends TestCase
         $idpSpUserVersionId =
             (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $this->createdAt, $this->createdAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->createdAt
+        );
 
         $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
         $this->assertCount(1, $resultArray);
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $this->createdAt, $this->createdAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->createdAt
+        );
         $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
         $this->assertCount(2, $resultArray);
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $this->createdAt, $this->createdAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->createdAt
+        );
         $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
         $this->assertCount(3, $resultArray);
 
@@ -669,7 +707,12 @@ class RepositoryTest extends TestCase
         $idpSpUserVersionId =
             (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
 
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $this->createdAt, $this->createdAt);
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->createdAt
+        );
         $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
         $this->assertCount(4, $resultArray);
 
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/StoreTest.php b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/StoreTest.php
index 9919d1faf25c5b7039c63054075fe269bf2501ae..5cbed489a9c73a83a2d798d1f600840aa00b73ad 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/StoreTest.php
+++ b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/StoreTest.php
@@ -54,6 +54,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @uses \SimpleSAML\Module\accounting\Entities\Activity\Bag
  * @uses \SimpleSAML\Module\accounting\Entities\Activity
  * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawActivity
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  *
  * @psalm-suppress all
  */
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php b/tests/src/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php
index 65b9ab9e940fffb0fd4a0806829cbbf71f026742..fa92aa61cf55074ef500e06aff7b025775bbdcf4 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php
+++ b/tests/src/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php
@@ -18,6 +18,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\State
  * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
  * @uses \SimpleSAML\Module\accounting\Helpers\NetworkHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class RawJobTest extends TestCase
 {
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php b/tests/src/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php
index fba787b148762a3f2079904f691b54ae3e4cfc72..30a427bb3d32f4e147a1e99acb3ed55106b1a96f 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php
+++ b/tests/src/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php
@@ -41,6 +41,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\State
  * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
  * @uses \SimpleSAML\Module\accounting\Helpers\NetworkHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class RepositoryTest extends TestCase
 {
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/StoreTest.php b/tests/src/Stores/Jobs/DoctrineDbal/StoreTest.php
index d4efbb85925d773f9e778e412b8b3be3776d22da..85d78ae3067fcef5cba7ea155cfa9a48d35456ab 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/StoreTest.php
+++ b/tests/src/Stores/Jobs/DoctrineDbal/StoreTest.php
@@ -41,6 +41,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\State
  * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
  * @uses \SimpleSAML\Module\accounting\Helpers\NetworkHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class StoreTest extends TestCase
 {
@@ -301,4 +302,28 @@ class StoreTest extends TestCase
 
         $this->assertNotNull($jobsStore->dequeue());
     }
+
+    public function testCanMarkFailedJob(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore = new Store($this->moduleConfiguration, $this->loggerStub, $this->factoryStub);
+        $jobsStore->runSetup();
+
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+        $queryBuilder->select('COUNT(id) as jobsCount')
+            ->from($jobsStore->getPrefixedTableNameFailedJobs())
+            ->fetchOne();
+
+        $this->assertSame(0, (int) $queryBuilder->executeQuery()->fetchOne());
+
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore->markFailedJob($this->jobStub);
+
+        $this->assertSame(1, (int) $queryBuilder->executeQuery()->fetchOne());
+
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore->markFailedJob($this->jobStub);
+
+        $this->assertSame(2, (int) $queryBuilder->executeQuery()->fetchOne());
+    }
 }
diff --git a/tests/src/Trackers/Authentication/DoctrineDbal/Versioned/TrackerTest.php b/tests/src/Trackers/Authentication/DoctrineDbal/Versioned/TrackerTest.php
index 7f64ac971b218236af493c6d878aeb32eda7eef3..ba0d0317a059ca2251c028ae7dc7cbd4dbccbf02 100644
--- a/tests/src/Trackers/Authentication/DoctrineDbal/Versioned/TrackerTest.php
+++ b/tests/src/Trackers/Authentication/DoctrineDbal/Versioned/TrackerTest.php
@@ -9,6 +9,7 @@ use SimpleSAML\Module\accounting\Entities\Activity;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
 use SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned\Tracker;
 use PHPUnit\Framework\TestCase;
@@ -26,6 +27,8 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
  * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store
  * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository
  * @uses \SimpleSAML\Module\accounting\Helpers\HashHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
  *
  * @psalm-suppress all
  */
@@ -43,6 +46,10 @@ class TrackerTest extends TestCase
      * @var \PHPUnit\Framework\MockObject\MockObject|Store
      */
     protected $dataStoreMock;
+    /**
+     * @var \PHPUnit\Framework\MockObject\Stub|HelpersManager
+     */
+    protected $helpersManagerStub;
 
     protected function setUp(): void
     {
@@ -51,6 +58,7 @@ class TrackerTest extends TestCase
             ->willReturn(ConnectionParameters::DBAL_SQLITE_MEMORY);
         $this->loggerMock = $this->createMock(LoggerInterface::class);
         $this->dataStoreMock = $this->createMock(Store::class);
+        $this->helpersManagerStub = $this->createStub(HelpersManager::class);
     }
 
     /**
@@ -64,6 +72,7 @@ class TrackerTest extends TestCase
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
                 ModuleConfiguration\ConnectionType::MASTER,
+                $this->helpersManagerStub,
                 $this->dataStoreMock
             )
         );
@@ -92,6 +101,7 @@ class TrackerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
+            $this->helpersManagerStub,
             $this->dataStoreMock
         );
 
@@ -112,6 +122,7 @@ class TrackerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
+            $this->helpersManagerStub,
             $this->dataStoreMock
         );
 
@@ -120,6 +131,26 @@ class TrackerTest extends TestCase
         $tracker->runSetup();
     }
 
+    public function testRunningSetupIfNotNeededLogsWarning(): void
+    {
+        $this->dataStoreMock->method('needsSetup')
+            ->willReturn(false);
+
+        $this->loggerMock->expects($this->once())
+            ->method('warning');
+
+        /** @psalm-suppress PossiblyInvalidArgument */
+        $tracker = new Tracker(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->helpersManagerStub,
+            $this->dataStoreMock
+        );
+
+        $tracker->runSetup();
+    }
+
     public function testGetConnectedServiceProviders(): void
     {
         $connectedOrganizationsBagStub = $this->createStub(ConnectedServiceProvider\Bag::class);
@@ -132,6 +163,7 @@ class TrackerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
+            $this->helpersManagerStub,
             $this->dataStoreMock
         );
 
@@ -152,6 +184,7 @@ class TrackerTest extends TestCase
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
+            $this->helpersManagerStub,
             $this->dataStoreMock
         );
 
diff --git a/tests/src/Trackers/Builders/AuthenticationDataTrackerBuilderTest.php b/tests/src/Trackers/Builders/AuthenticationDataTrackerBuilderTest.php
index 6fd033f2cede8c4c430ee49c18c4dcd2d0a96003..cf3bae0c93acf0b4ca09469393c815bcbbaaab51 100644
--- a/tests/src/Trackers/Builders/AuthenticationDataTrackerBuilderTest.php
+++ b/tests/src/Trackers/Builders/AuthenticationDataTrackerBuilderTest.php
@@ -8,6 +8,7 @@ use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Exceptions\Exception;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
 use PHPUnit\Framework\TestCase;
 use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
@@ -16,6 +17,7 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 /**
  * @covers \SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder
  * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfigurationHelper
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  *
  * @psalm-suppress all
  */
@@ -31,6 +33,7 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
     protected $moduleConfigurationStub;
 
     protected AuthenticationDataTrackerInterface $trackerStub;
+    protected HelpersManager $helpersManager;
 
     protected function setUp(): void
     {
@@ -39,6 +42,8 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
             ->willReturn(ConnectionParameters::DBAL_SQLITE_MEMORY);
         $this->loggerMock = $this->createMock(LoggerInterface::class);
 
+        $this->helpersManager = new HelpersManager();
+
         $this->trackerStub = new class implements AuthenticationDataTrackerInterface {
             public static function build(
                 ModuleConfiguration $moduleConfiguration,
@@ -66,7 +71,11 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
     {
         $this->assertInstanceOf(
             AuthenticationDataTrackerBuilder::class,
-            new AuthenticationDataTrackerBuilder($this->moduleConfigurationStub, $this->loggerMock)
+            new AuthenticationDataTrackerBuilder(
+                $this->moduleConfigurationStub,
+                $this->loggerMock,
+                $this->helpersManager
+            )
         );
     }
 
@@ -74,7 +83,8 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
     {
         $authenticationDataTrackerBuilder = new AuthenticationDataTrackerBuilder(
             $this->moduleConfigurationStub,
-            $this->loggerMock
+            $this->loggerMock,
+            $this->helpersManager
         );
 
         $trackerClass = get_class($this->trackerStub);
@@ -86,7 +96,8 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
     {
         $authenticationDataTrackerBuilder = new AuthenticationDataTrackerBuilder(
             $this->moduleConfigurationStub,
-            $this->loggerMock
+            $this->loggerMock,
+            $this->helpersManager
         );
 
         $this->expectException(Exception::class);