diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..94b1bc5594d8e3ba20313df5e6402327e6a3e975
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.itvsh.kop.user;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.assertj.core.api.Condition;
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import de.itvsh.kop.user.common.errorhandling.ResourceNotFoundException;
+import io.quarkus.mongodb.panache.PanacheQuery;
+import io.quarkus.mongodb.panache.common.PanacheUpdate;
+
+class UserRepositoryTest {
+	@Spy
+	UserRepository userRepository;
+
+	@Mock
+	PanacheQuery<User> panacheQuery;
+
+	User user = UserTestFactory.create();
+
+	@DisplayName("Test updating users")
+	@Nested
+	class TestUpdate {
+
+		@Test
+		void shouldCallUpdate() {
+			doReturn(UserTestFactory.create()).when(userRepository).findById(any(ObjectId.class));
+			doNothing().when(userRepository).update(any(User.class));
+
+			userRepository.updateUser(UserTestFactory.create());
+
+			verify(userRepository, times(2)).findById(any(ObjectId.class));
+			verify(userRepository).update(any(User.class));
+		}
+
+		@Test
+		void shouldMarkUserAsDeleted() {
+			PanacheUpdate update = mock(PanacheUpdate.class);
+			doReturn(update).when(userRepository).update(anyString(), anyBoolean());
+			long timestamp = new Date().getTime();
+
+			userRepository.updateUnsyncedUsers(timestamp);
+
+			verify(userRepository).update(User.DELETED_FIELD, true);
+			verify(update).where("lastSyncTimestamp < ?1", timestamp);
+		}
+	}
+
+	@DisplayName("Test loading users")
+	@Nested
+	class TestFind {
+
+		@Test
+		void shouldFindByExternalId() {
+			doReturn(Optional.of(user)).when(panacheQuery).firstResultOptional();
+			doReturn(panacheQuery).when(userRepository).find(anyString(), anyString());
+
+			var userOpt = userRepository.findByExternalId(UserTestFactory.EXTERNAL_ID);
+
+			assertThat(userOpt).isPresent().get().isEqualTo(user);
+		}
+
+		@Test
+		void shouldFindByEmail() {
+			doReturn(Optional.of(user)).when(panacheQuery).firstResultOptional();
+			doReturn(panacheQuery).when(userRepository).find(anyString(), anyString());
+
+			var userOpt = userRepository.findByEmail(UserTestFactory.EMAIL);
+
+			assertThat(userOpt).isPresent().get().isEqualTo(user);
+		}
+
+		@Test
+		void shouldFindById() {
+			doReturn(Optional.of(user)).when(userRepository).findByIdOptional(UserTestFactory.ID);
+
+			var userOpt = userRepository.findById(UserTestFactory.ID.toHexString());
+
+			assertThat(userOpt).isPresent().get().isEqualTo(user);
+		}
+
+		@DisplayName("handle loading list of users")
+		@Nested
+		class TestFindUsers {
+			Condition<User> cond = new Condition<>(value -> value.equals(user), "User expected");
+
+			@Mock
+			PanacheQuery<?> rangeQuery;
+
+			@BeforeEach
+			void init() {
+				doReturn(List.of(UserTestFactory.create()).stream()).when(rangeQuery).stream();
+				doReturn(rangeQuery).when(panacheQuery).range(anyInt(), anyInt());
+			}
+
+			@Test
+			void shouldFindUsers() {
+				doReturn(panacheQuery).when(userRepository).find(anyString(), anyString());
+
+				var userStream = userRepository.findUsers(UserTestFactory.USER_NAME, 1);
+
+				assertThat(userStream).areExactly(0, cond);
+			}
+
+			@Test
+			void shouldFindDeletedUsers() {
+				doReturn(panacheQuery).when(userRepository).find(anyString(), anyString(), anyBoolean());
+
+				var userStream = userRepository.findUsers(UserTestFactory.USER_NAME, true, 1);
+
+				assertThat(userStream).areExactly(0, cond);
+			}
+		}
+	}
+
+	@DisplayName("Test refreshing a user")
+	@Nested
+	class TestRefresh {
+		@Test
+		void shouldRefreshUserById() {
+			doReturn(Optional.of(user)).when(userRepository).findByIdOptional(UserTestFactory.ID);
+			doReturn(Optional.of(user)).when(panacheQuery).firstResultOptional();
+			doReturn(panacheQuery).when(userRepository).find(anyString(), anyString());
+
+			var refreshedUser = userRepository.refresh(user);
+
+			assertThat(refreshedUser).isEqualTo(user);
+		}
+
+		@Test
+		void shouldRefreshUserByExternalId() {
+			doReturn(Optional.empty()).when(userRepository).findByIdOptional(any(ObjectId.class));
+			doReturn(Optional.of(user)).when(userRepository).findByExternalId(anyString());
+
+			var refreshedUser = userRepository.refresh(UserTestFactory.create());
+
+			assertThat(refreshedUser).isEqualTo(user);
+		}
+
+		@Test
+		void shouldThrowResourceNotFound() {
+			doReturn(Optional.empty()).when(userRepository).findByIdOptional(any(ObjectId.class));
+			doReturn(Optional.empty()).when(userRepository).findByExternalId(anyString());
+
+			assertThatExceptionOfType(ResourceNotFoundException.class).isThrownBy(() -> userRepository.refresh(UserTestFactory.create())); // NOSONAR
+		}
+	}
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/common/lock/LockRepositoryTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/common/lock/LockRepositoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd8e0f5c0da7034729eb80ec1e030c76766aff15
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/common/lock/LockRepositoryTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.itvsh.kop.user.common.lock;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.time.Instant;
+import java.util.Optional;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import io.quarkus.mongodb.panache.PanacheQuery;
+
+class LockRepositoryTest {
+	@Spy
+	LockRepository lockRepository;
+
+	@Mock
+	PanacheQuery<Lock> panacheFindQuery;
+
+	@DisplayName("Test loading sync lock")
+	@Nested
+	class TestGetLock {
+		private static final Lock LOCK = LockTestFactory.create();
+
+		@BeforeEach
+		void init() {
+			doReturn(panacheFindQuery).when(lockRepository).find(anyString(), anyLong());
+			doReturn(Optional.of(LOCK)).when(panacheFindQuery).firstResultOptional();
+		}
+
+		@Test
+		void shouldGetOldLock() {
+			var lockOptional = lockRepository.findLockBefore(Instant.ofEpochMilli(LOCK.getTimestamp()));
+
+			assertThat(lockOptional).isPresent().get().hasFieldOrPropertyWithValue(Lock.TIMESTAMP_FIELD, LOCK.getTimestamp());
+		}
+
+		@Test
+		void shouldGetLock() {
+			var lockOptional = lockRepository.findLock(Instant.ofEpochMilli(LOCK.getTimestamp()));
+
+			assertThat(lockOptional).isPresent().get().hasFieldOrPropertyWithValue(Lock.TIMESTAMP_FIELD, LOCK.getTimestamp());
+		}
+	}
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/keycloak/KeycloakProviderTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/keycloak/KeycloakProviderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b12dcbdd5d082eb95c702715ca159fd6f7565a44
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/keycloak/KeycloakProviderTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.itvsh.kop.user.keycloak;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.lang.reflect.Field;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import lombok.SneakyThrows;
+
+class KeycloakProviderTest {
+	@InjectMocks
+	KeycloakProvider keycloakProvider;
+	@Mock
+	KeycloakApiProperties properties;
+
+	@DisplayName("Test providing keycloak admin client")
+	@Nested
+	class TestProvider {
+		@BeforeEach
+		void initProperties() {
+			setServerUrl();
+			when(properties.realm()).thenReturn("realm");
+			when(properties.user()).thenReturn("user");
+			when(properties.password()).thenReturn("password");
+		}
+
+		@SneakyThrows
+		private void setServerUrl() {
+			Field url = keycloakProvider.getClass().getDeclaredField("keycloakUrl");
+			url.set(keycloakProvider, "url");
+		}
+
+		@Test
+		void shouldCreateRealmResource() {
+			var resource = keycloakProvider.provide();
+
+			assertThat(resource).isNotNull();
+		}
+	}
+}