test: implement unit tests for RabbitMQ RPC endpoint
This commit is contained in:
@@ -62,6 +62,8 @@ dependencyManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
|
||||||
|
val mockitoAgent = configurations.create("mockitoAgent")
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
||||||
@@ -79,9 +81,11 @@ dependencies {
|
|||||||
testImplementation("io.projectreactor:reactor-test")
|
testImplementation("io.projectreactor:reactor-test")
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
testImplementation("com.squareup.okhttp3:mockwebserver:4.12.0")
|
testImplementation("com.squareup.okhttp3:mockwebserver:4.12.0")
|
||||||
|
mockitoAgent("org.mockito:mockito-core") { isTransitive = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Test> {
|
tasks.withType<Test> {
|
||||||
|
jvmArgs("-javaagent:${mockitoAgent.asPath}")
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
// generate jacoco test report
|
// generate jacoco test report
|
||||||
finalizedBy("jacocoTestReport")
|
finalizedBy("jacocoTestReport")
|
||||||
|
|||||||
+28
-24
@@ -14,7 +14,6 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for endpoint of auth-service over RabbitMQ.
|
* Abstract class for endpoint of auth-service over RabbitMQ.
|
||||||
@@ -34,7 +33,7 @@ public abstract class AbstractEndpoint implements AutoCloseable {
|
|||||||
* Initialize this abstract class.
|
* Initialize this abstract class.
|
||||||
*/
|
*/
|
||||||
public AbstractEndpoint(
|
public AbstractEndpoint(
|
||||||
@RequestParam final CleverMicroAmqpClient client,
|
final CleverMicroAmqpClient client,
|
||||||
final ObjectMapper objectMapper,
|
final ObjectMapper objectMapper,
|
||||||
final String routingKey
|
final String routingKey
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
@@ -51,33 +50,38 @@ public abstract class AbstractEndpoint implements AutoCloseable {
|
|||||||
);
|
);
|
||||||
this.handlerTag = client.getHandlerManager().registerHandler(
|
this.handlerTag = client.getHandlerManager().registerHandler(
|
||||||
this.queueName, "", 1,
|
this.queueName, "", 1,
|
||||||
ctx -> new Thread(() -> {
|
ctx -> new Thread(() -> handleRabbitMessage(ctx)).start(),
|
||||||
try {
|
|
||||||
final var req = parseRequest(ctx);
|
|
||||||
if (req == null) {
|
|
||||||
throw new EndpointBadRequestException(
|
|
||||||
"Malformed request, cannot be parsed");
|
|
||||||
}
|
|
||||||
handleRequest(ctx, req);
|
|
||||||
} catch (final EndpointException ex) {
|
|
||||||
final var resp = EndpointResponses.error(ex);
|
|
||||||
reply(ctx, resp);
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
log.error(
|
|
||||||
"Error when handling request for queue {}, only refill availability",
|
|
||||||
this.queueName, t
|
|
||||||
);
|
|
||||||
final var resp = EndpointResponses.internalServerError(
|
|
||||||
"Error when handling the request", t);
|
|
||||||
reply(ctx, resp);
|
|
||||||
}
|
|
||||||
}).start(),
|
|
||||||
tag -> log.info("Endpoint canceled for queue {}: handlerTag={}",
|
tag -> log.info("Endpoint canceled for queue {}: handlerTag={}",
|
||||||
this.queueName, tag)
|
this.queueName, tag)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EndpointRequest parseRequest(final HandlerContext ctx) {
|
// VisibleForTesting
|
||||||
|
void handleRabbitMessage(final HandlerContext ctx) {
|
||||||
|
try {
|
||||||
|
final var req = parseRequest(ctx);
|
||||||
|
if (req == null) {
|
||||||
|
throw new EndpointBadRequestException(
|
||||||
|
"Malformed request, cannot be parsed");
|
||||||
|
}
|
||||||
|
handleRequest(ctx, req);
|
||||||
|
} catch (final EndpointException ex) {
|
||||||
|
final var resp = EndpointResponses.error(ex);
|
||||||
|
reply(ctx, resp);
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
log.error(
|
||||||
|
"Error when handling request for queue {}, only refill availability",
|
||||||
|
this.queueName, t
|
||||||
|
);
|
||||||
|
final var resp = EndpointResponses.internalServerError(
|
||||||
|
"Error when handling the request", t);
|
||||||
|
reply(ctx, resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// VisibleForTesting
|
||||||
|
EndpointRequest parseRequest(final HandlerContext ctx) {
|
||||||
InputStream reqStream = null;
|
InputStream reqStream = null;
|
||||||
if (ctx.getMessageBodySingleStream().isPresent()) {
|
if (ctx.getMessageBodySingleStream().isPresent()) {
|
||||||
reqStream = ctx.getMessageBodySingleStream().get();
|
reqStream = ctx.getMessageBodySingleStream().get();
|
||||||
|
|||||||
+76
-82
@@ -6,14 +6,15 @@ import com.cleverthis.authservice.dto.keycloakadmin.KeycloakInvitationRequest;
|
|||||||
import com.cleverthis.authservice.exception.RegistrationException;
|
import com.cleverthis.authservice.exception.RegistrationException;
|
||||||
import com.cleverthis.authservice.exception.ServiceUnavailableException;
|
import com.cleverthis.authservice.exception.ServiceUnavailableException;
|
||||||
import com.cleverthis.authservice.exception.UserAlreadyExistsException;
|
import com.cleverthis.authservice.exception.UserAlreadyExistsException;
|
||||||
|
import jakarta.ws.rs.ClientErrorException;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.keycloak.admin.client.Keycloak;
|
import org.keycloak.admin.client.Keycloak;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.GroupRepresentation;
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
import org.keycloak.representations.idm.MemberRepresentation;
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
@@ -21,6 +22,7 @@ import org.keycloak.representations.idm.OrganizationRepresentation;
|
|||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.publisher.MonoSink;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to interact with Keycloak Admin API.
|
* Service to interact with Keycloak Admin API.
|
||||||
@@ -46,39 +48,6 @@ public class KeycloakAdminReactiveClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the internal UUID of a Keycloak client given its public clientId.
|
|
||||||
*
|
|
||||||
* @param publicClientId The human-readable client_id (e.g., "cleverswarm-client").
|
|
||||||
* @return Mono emitting the internal UUID string.
|
|
||||||
*/
|
|
||||||
public Mono<String> getClientInternalId(String publicClientId) {
|
|
||||||
log.debug("Fetching internal ID for client: {}", publicClientId);
|
|
||||||
|
|
||||||
return Mono.just(this.keycloak.realm(this.realm)
|
|
||||||
.clients().findByClientId(publicClientId))
|
|
||||||
.flatMap(result -> {
|
|
||||||
if (result.isEmpty()) {
|
|
||||||
log.warn("Client id '{}' not found in Keycloak", publicClientId);
|
|
||||||
return Mono.error(new ServiceUnavailableException(
|
|
||||||
"Client not found in Keycloak: " + publicClientId));
|
|
||||||
} else {
|
|
||||||
return Mono.just(result.getFirst());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(ClientRepresentation::getId)
|
|
||||||
.doOnSuccess(id -> log.debug("Found internal ID '{}' for client '{}'", id,
|
|
||||||
publicClientId))
|
|
||||||
.onErrorMap(t -> {
|
|
||||||
log.error("Failed to get client '{}'",
|
|
||||||
publicClientId, t);
|
|
||||||
return new ServiceUnavailableException(
|
|
||||||
"Could not fetch client details from Keycloak, clientPublicId="
|
|
||||||
+ publicClientId, t);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new organization in Keycloak.
|
* Creates a new organization in Keycloak.
|
||||||
*
|
*
|
||||||
@@ -125,8 +94,15 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @return A Mono emitting the representation of the found organization.
|
* @return A Mono emitting the representation of the found organization.
|
||||||
*/
|
*/
|
||||||
public Mono<OrganizationRepresentation> getOrganizationById(String orgId) {
|
public Mono<OrganizationRepresentation> getOrganizationById(String orgId) {
|
||||||
return Mono.just(this.keycloak.realm(this.realm)
|
return Mono.create(sink -> {
|
||||||
.organizations().get(orgId).toRepresentation());
|
try {
|
||||||
|
sink.success(this.keycloak.realm(this.realm)
|
||||||
|
.organizations().get(orgId).toRepresentation());
|
||||||
|
} catch (final NotFoundException ex) {
|
||||||
|
sink.error(new NoSuchElementException(
|
||||||
|
"Organization with id " + orgId + " not exists", ex));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,8 +129,6 @@ public class KeycloakAdminReactiveClient {
|
|||||||
} else {
|
} else {
|
||||||
sink.success();
|
sink.success();
|
||||||
}
|
}
|
||||||
} catch (final Throwable t) {
|
|
||||||
sink.error(t);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -179,8 +153,6 @@ public class KeycloakAdminReactiveClient {
|
|||||||
} else {
|
} else {
|
||||||
sink.success();
|
sink.success();
|
||||||
}
|
}
|
||||||
} catch (final Throwable t) {
|
|
||||||
sink.error(t);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -211,8 +183,6 @@ public class KeycloakAdminReactiveClient {
|
|||||||
} else {
|
} else {
|
||||||
sink.success();
|
sink.success();
|
||||||
}
|
}
|
||||||
} catch (final Throwable t) {
|
|
||||||
sink.error(t);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -224,9 +194,16 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @return A Mono emitting a List of user representations for the members.
|
* @return A Mono emitting a List of user representations for the members.
|
||||||
*/
|
*/
|
||||||
public Mono<List<MemberRepresentation>> getOrganizationMembers(String orgId) {
|
public Mono<List<MemberRepresentation>> getOrganizationMembers(String orgId) {
|
||||||
return Mono.just(this.keycloak.realm(this.realm)
|
return Mono.create(sink -> {
|
||||||
.organizations().get(orgId)
|
try {
|
||||||
.members().list(null, Integer.MAX_VALUE));
|
sink.success(this.keycloak.realm(this.realm)
|
||||||
|
.organizations().get(orgId)
|
||||||
|
.members().list(null, Integer.MAX_VALUE));
|
||||||
|
} catch (final NotFoundException ex) {
|
||||||
|
sink.error(new NoSuchElementException(
|
||||||
|
"Organization with id " + orgId + " not exists", ex));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,8 +230,6 @@ public class KeycloakAdminReactiveClient {
|
|||||||
} else {
|
} else {
|
||||||
sink.success();
|
sink.success();
|
||||||
}
|
}
|
||||||
} catch (final Throwable t) {
|
|
||||||
sink.error(t);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -281,8 +256,6 @@ public class KeycloakAdminReactiveClient {
|
|||||||
} else {
|
} else {
|
||||||
sink.success();
|
sink.success();
|
||||||
}
|
}
|
||||||
} catch (final Throwable t) {
|
|
||||||
sink.error(t);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -310,8 +283,6 @@ public class KeycloakAdminReactiveClient {
|
|||||||
} else {
|
} else {
|
||||||
sink.success();
|
sink.success();
|
||||||
}
|
}
|
||||||
} catch (final Throwable t) {
|
|
||||||
sink.error(t);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -323,8 +294,12 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @return A Mono emitting the group representation.
|
* @return A Mono emitting the group representation.
|
||||||
*/
|
*/
|
||||||
public Mono<GroupRepresentation> getGroupById(String groupId) {
|
public Mono<GroupRepresentation> getGroupById(String groupId) {
|
||||||
return Mono.just(
|
return Mono.create((Consumer<MonoSink<GroupRepresentation>>) sink ->
|
||||||
this.keycloak.realm(this.realm).groups().group(groupId).toRepresentation());
|
sink.success(this.keycloak.realm(this.realm)
|
||||||
|
.groups().group(groupId).toRepresentation()))
|
||||||
|
.onErrorMap(NotFoundException.class,
|
||||||
|
ex -> new NoSuchElementException(
|
||||||
|
"Group with id " + groupId + " not exists", ex));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -334,9 +309,13 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @return A Mono emitting a list of sub-group representations.
|
* @return A Mono emitting a list of sub-group representations.
|
||||||
*/
|
*/
|
||||||
public Mono<List<GroupRepresentation>> listSubGroups(String parentGroupId) {
|
public Mono<List<GroupRepresentation>> listSubGroups(String parentGroupId) {
|
||||||
return Mono.just(this.keycloak.realm(this.realm)
|
return Mono.create((Consumer<MonoSink<List<GroupRepresentation>>>) sink ->
|
||||||
.groups().group(parentGroupId)
|
sink.success(this.keycloak.realm(this.realm)
|
||||||
.getSubGroups(null, Integer.MAX_VALUE, false));
|
.groups().group(parentGroupId)
|
||||||
|
.getSubGroups(null, Integer.MAX_VALUE, false)))
|
||||||
|
.onErrorMap(NotFoundException.class,
|
||||||
|
ex -> new NoSuchElementException(
|
||||||
|
"Group with id " + parentGroupId + " not exists", ex));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -347,10 +326,14 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @return A Mono that completes on successful update.
|
* @return A Mono that completes on successful update.
|
||||||
*/
|
*/
|
||||||
public Mono<Void> updateGroup(String groupId, GroupRepresentation group) {
|
public Mono<Void> updateGroup(String groupId, GroupRepresentation group) {
|
||||||
return Mono.create(sink -> {
|
return Mono
|
||||||
this.keycloak.realm(this.realm).groups().group(groupId).update(group);
|
.create((Consumer<MonoSink<Void>>) sink -> {
|
||||||
sink.success();
|
this.keycloak.realm(this.realm).groups().group(groupId).update(group);
|
||||||
});
|
sink.success();
|
||||||
|
})
|
||||||
|
.onErrorMap(NotFoundException.class, ex -> new NoSuchElementException(
|
||||||
|
"Group with id " + groupId + " not exists"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -360,10 +343,14 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @return A Mono that completes on successful deletion.
|
* @return A Mono that completes on successful deletion.
|
||||||
*/
|
*/
|
||||||
public Mono<Void> deleteGroup(String groupId) {
|
public Mono<Void> deleteGroup(String groupId) {
|
||||||
return Mono.create(sink -> {
|
return Mono
|
||||||
this.keycloak.realm(this.realm).groups().group(groupId).remove();
|
.create((Consumer<MonoSink<Void>>) sink -> {
|
||||||
sink.success();
|
this.keycloak.realm(this.realm).groups().group(groupId).remove();
|
||||||
});
|
sink.success();
|
||||||
|
})
|
||||||
|
.onErrorMap(NotFoundException.class, ex -> new NoSuchElementException(
|
||||||
|
"Group with id " + groupId + " not exists"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -375,10 +362,14 @@ public class KeycloakAdminReactiveClient {
|
|||||||
*/
|
*/
|
||||||
public Mono<Void> addMemberToGroup(String groupId, String userId) {
|
public Mono<Void> addMemberToGroup(String groupId, String userId) {
|
||||||
// Keycloak API for adding a user to a group is a PUT on the user's groups link
|
// Keycloak API for adding a user to a group is a PUT on the user's groups link
|
||||||
return Mono.create(sink -> {
|
return Mono
|
||||||
this.keycloak.realm(this.realm).users().get(userId).joinGroup(groupId);
|
.create((Consumer<MonoSink<Void>>) sink -> {
|
||||||
sink.success();
|
this.keycloak.realm(this.realm).users().get(userId).joinGroup(groupId);
|
||||||
});
|
sink.success();
|
||||||
|
})
|
||||||
|
.onErrorMap(ClientErrorException.class, ex -> new IllegalArgumentException(
|
||||||
|
"Failed to add user with id " + userId + " to group with id " + groupId
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -388,8 +379,11 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @return A Mono emitting a list of user representations.
|
* @return A Mono emitting a list of user representations.
|
||||||
*/
|
*/
|
||||||
public Mono<List<UserRepresentation>> getGroupMembers(String groupId) {
|
public Mono<List<UserRepresentation>> getGroupMembers(String groupId) {
|
||||||
return Mono.just(this.keycloak.realm(this.realm).groups().group(groupId).members());
|
return Mono.create((Consumer<MonoSink<List<UserRepresentation>>>) sink -> sink.success(
|
||||||
|
this.keycloak.realm(this.realm).groups().group(groupId).members()))
|
||||||
|
.onErrorMap(NotFoundException.class, ex -> new NoSuchElementException(
|
||||||
|
"Group with id " + groupId + " not exist", ex
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -400,10 +394,14 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @return A Mono that completes when the user is removed from the group.
|
* @return A Mono that completes when the user is removed from the group.
|
||||||
*/
|
*/
|
||||||
public Mono<Void> removeMemberFromGroup(String groupId, String userId) {
|
public Mono<Void> removeMemberFromGroup(String groupId, String userId) {
|
||||||
return Mono.create(sink -> {
|
return Mono
|
||||||
this.keycloak.realm(this.realm).users().get(userId).leaveGroup(groupId);
|
.create((Consumer<MonoSink<Void>>) sink -> {
|
||||||
sink.success();
|
this.keycloak.realm(this.realm).users().get(userId).leaveGroup(groupId);
|
||||||
});
|
sink.success();
|
||||||
|
})
|
||||||
|
.onErrorMap(ClientErrorException.class, ex -> new IllegalArgumentException(
|
||||||
|
"Failed to remove user with id " + userId + " from group with id " + groupId
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -455,8 +453,6 @@ public class KeycloakAdminReactiveClient {
|
|||||||
"User registration failed due to Keycloak error."
|
"User registration failed due to Keycloak error."
|
||||||
+ " Status: " + resp.getStatus()));
|
+ " Status: " + resp.getStatus()));
|
||||||
}
|
}
|
||||||
} catch (final Throwable t) {
|
|
||||||
sink.error(t);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -468,12 +464,10 @@ public class KeycloakAdminReactiveClient {
|
|||||||
* @throws NoSuchElementException in mono if keycloak returns 404.
|
* @throws NoSuchElementException in mono if keycloak returns 404.
|
||||||
*/
|
*/
|
||||||
public Mono<UserRepresentation> getUserDetails(String userId) {
|
public Mono<UserRepresentation> getUserDetails(String userId) {
|
||||||
try {
|
return Mono.create((Consumer<MonoSink<UserRepresentation>>) sink -> sink.success(
|
||||||
return Mono.just(this.keycloak.realm(this.realm)
|
this.keycloak.realm(this.realm).users().get(userId).toRepresentation()))
|
||||||
.users().get(userId).toRepresentation());
|
.onErrorMap(NotFoundException.class,
|
||||||
} catch (final NotFoundException ex) {
|
ex -> new NoSuchElementException("User with id " + userId + " not exists", ex));
|
||||||
return Mono.error(new NoSuchElementException("User with id " + userId + " not exists"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+238
@@ -0,0 +1,238 @@
|
|||||||
|
package com.cleverthis.authservice.controller.rabbitmq;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.ArgumentMatchers.notNull;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.cleverthis.authservice.controller.rabbitmq.exception.EndpointException;
|
||||||
|
import com.cleverthis.authservice.controller.rabbitmq.model.EndpointErrorResponse;
|
||||||
|
import com.cleverthis.authservice.controller.rabbitmq.model.EndpointOkResponse;
|
||||||
|
import com.cleverthis.authservice.controller.rabbitmq.model.EndpointRequest;
|
||||||
|
import com.cleverthis.clevermicro.client.amqp.CleverMicroAmqpClient;
|
||||||
|
import com.cleverthis.clevermicro.client.amqp.handler.HandlerContext;
|
||||||
|
import com.cleverthis.clevermicro.client.amqp.handler.HandlerSubmodule;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.rabbitmq.client.BuiltinExchangeType;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
class AbstractEndpointTest {
|
||||||
|
private final CleverMicroAmqpClient client = Mockito.mock(CleverMicroAmqpClient.class);
|
||||||
|
final ObjectMapper objectMapper = Mockito.spy(new ObjectMapper());
|
||||||
|
|
||||||
|
private class TestEndpoint extends AbstractEndpoint {
|
||||||
|
|
||||||
|
TestEndpoint() throws Exception {
|
||||||
|
super(
|
||||||
|
AbstractEndpointTest.this.client,
|
||||||
|
AbstractEndpointTest.this.objectMapper,
|
||||||
|
"test-key"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The throws is needed for mocked object to throw exception during test
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
|
@Override
|
||||||
|
protected void handleRequest(
|
||||||
|
final HandlerContext ctx, final EndpointRequest request
|
||||||
|
) throws EndpointException {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final HandlerSubmodule handlerSubmodule = Mockito.mock(HandlerSubmodule.class);
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
when(this.client.getHandlerManager()).thenReturn(this.handlerSubmodule);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInitAndClose() throws Exception {
|
||||||
|
when(this.client.getHandlerManager().registerHandler(
|
||||||
|
anyString(), eq(""), eq(1),
|
||||||
|
notNull(), notNull()
|
||||||
|
)).thenReturn("test-tag");
|
||||||
|
final var testEndpoint = new TestEndpoint();
|
||||||
|
verify(this.client.getHandlerManager()).declareExchange(
|
||||||
|
"cleverthis.clevermicro.auth.endpoints", BuiltinExchangeType.DIRECT
|
||||||
|
);
|
||||||
|
verify(this.client.getHandlerManager()).declareQueue(
|
||||||
|
"cleverthis.clevermicro.auth.endpoints.test-key",
|
||||||
|
true, false, false, Map.of()
|
||||||
|
);
|
||||||
|
verify(this.client.getHandlerManager()).bindQueue(
|
||||||
|
"cleverthis.clevermicro.auth.endpoints.test-key",
|
||||||
|
"cleverthis.clevermicro.auth.endpoints",
|
||||||
|
"test-key", Map.of()
|
||||||
|
);
|
||||||
|
|
||||||
|
testEndpoint.close();
|
||||||
|
verify(this.client.getHandlerManager()).cancelHandler("test-tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testHandler() throws Throwable {
|
||||||
|
final var testEndpoint = Mockito.spy(new TestEndpoint());
|
||||||
|
final var handlerContext = Mockito.mock(HandlerContext.class);
|
||||||
|
|
||||||
|
// silent reply method, it will be tested separately
|
||||||
|
doNothing().when(testEndpoint).reply(eq(handlerContext), notNull());
|
||||||
|
|
||||||
|
// parse req null
|
||||||
|
doReturn(null).when(testEndpoint).parseRequest(notNull());
|
||||||
|
testEndpoint.handleRabbitMessage(handlerContext);
|
||||||
|
verify(testEndpoint).reply(eq(handlerContext), argThat(it -> {
|
||||||
|
final var resp = (EndpointErrorResponse) it;
|
||||||
|
return resp.getException().equals("cleverthis.clevermicro.bad_request")
|
||||||
|
&& resp.getMessage().equals("Malformed request, cannot be parsed");
|
||||||
|
}));
|
||||||
|
|
||||||
|
// path: parse req ok
|
||||||
|
Mockito.clearInvocations(testEndpoint);
|
||||||
|
// handle req ok
|
||||||
|
doReturn(EndpointRequest.builder().build()).when(testEndpoint).parseRequest(notNull());
|
||||||
|
doNothing().when(testEndpoint).handleRequest(notNull(), notNull());
|
||||||
|
assertDoesNotThrow(() -> testEndpoint.handleRabbitMessage(handlerContext));
|
||||||
|
verify(testEndpoint, never()).reply(notNull(), notNull());
|
||||||
|
|
||||||
|
// handle req endpoint exception
|
||||||
|
Mockito.clearInvocations(testEndpoint);
|
||||||
|
doThrow(new EndpointException("test", "message"))
|
||||||
|
.when(testEndpoint).handleRequest(notNull(), notNull());
|
||||||
|
assertDoesNotThrow(() -> testEndpoint.handleRabbitMessage(handlerContext));
|
||||||
|
verify(testEndpoint).reply(notNull(), argThat(it -> {
|
||||||
|
final var resp = (EndpointErrorResponse) it;
|
||||||
|
return resp.getException().equals("test")
|
||||||
|
&& resp.getMessage().equals("message");
|
||||||
|
}));
|
||||||
|
|
||||||
|
// handle req any exception
|
||||||
|
Mockito.clearInvocations(testEndpoint);
|
||||||
|
doThrow(new RuntimeException("test"))
|
||||||
|
.when(testEndpoint).handleRequest(notNull(), notNull());
|
||||||
|
assertDoesNotThrow(() -> testEndpoint.handleRabbitMessage(handlerContext));
|
||||||
|
verify(testEndpoint).reply(notNull(), argThat(it -> {
|
||||||
|
final var resp = (EndpointErrorResponse) it;
|
||||||
|
return resp.getException().equals("cleverthis.clevermicro.server_internal_error");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testParseRequest_SingleStream() throws Exception {
|
||||||
|
//noinspection resource
|
||||||
|
final var testEndpoint = new TestEndpoint();
|
||||||
|
final var handlerContext = Mockito.mock(HandlerContext.class);
|
||||||
|
final var inputStream = Mockito.mock(InputStream.class);
|
||||||
|
|
||||||
|
when(handlerContext.getMessageBodySingleStream()).thenReturn(Optional.of(inputStream));
|
||||||
|
final var req = EndpointRequest.builder().method("testMethod").build();
|
||||||
|
doReturn(req).when(this.objectMapper).readValue(inputStream, EndpointRequest.class);
|
||||||
|
|
||||||
|
final var result = testEndpoint.parseRequest(handlerContext);
|
||||||
|
assertEquals(req, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testParseRequest_MultiStream() throws Exception {
|
||||||
|
//noinspection resource
|
||||||
|
final var testEndpoint = new TestEndpoint();
|
||||||
|
final var handlerContext = Mockito.mock(HandlerContext.class);
|
||||||
|
final var inputStream = Mockito.mock(InputStream.class);
|
||||||
|
|
||||||
|
when(handlerContext.getMessageBodyMultiStream()).thenReturn(Optional.of(
|
||||||
|
Map.of("request", inputStream)
|
||||||
|
));
|
||||||
|
final var req = EndpointRequest.builder().method("testMethod").build();
|
||||||
|
doReturn(req).when(this.objectMapper).readValue(inputStream, EndpointRequest.class);
|
||||||
|
|
||||||
|
final var result = testEndpoint.parseRequest(handlerContext);
|
||||||
|
assertEquals(req, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReply_SuccessfulSerialization() throws Exception {
|
||||||
|
//noinspection resource
|
||||||
|
final var testEndpoint = new TestEndpoint();
|
||||||
|
final var handlerContext = Mockito.mock(HandlerContext.class);
|
||||||
|
final var response = Map.of("key", "value");
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> testEndpoint.reply(handlerContext, response));
|
||||||
|
verify(handlerContext).finish("""
|
||||||
|
{
|
||||||
|
"key" : "value"
|
||||||
|
}
|
||||||
|
""".trim().getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReply_SerializationFailure() throws Exception {
|
||||||
|
//noinspection resource
|
||||||
|
final var testEndpoint = new TestEndpoint();
|
||||||
|
final var handlerContext = Mockito.mock(HandlerContext.class);
|
||||||
|
final var response = mock(EndpointOkResponse.class);
|
||||||
|
// throw error when accessing fields, causing jackson failed to serialize
|
||||||
|
when(response.getResult()).thenThrow(new RuntimeException("test"));
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> testEndpoint.reply(handlerContext, response));
|
||||||
|
verify(handlerContext, never()).finish(any());
|
||||||
|
verify(handlerContext).refillAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReply_ReplyFailure() throws Exception {
|
||||||
|
//noinspection resource
|
||||||
|
final var testEndpoint = new TestEndpoint();
|
||||||
|
final var handlerContext = Mockito.mock(HandlerContext.class);
|
||||||
|
final var response = Map.of("key", "value");
|
||||||
|
|
||||||
|
doThrow(new IOException("Reply error")).when(handlerContext).finish(notNull());
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> testEndpoint.reply(handlerContext, response));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testParseRequest_InvalidRequest() throws Exception {
|
||||||
|
//noinspection resource
|
||||||
|
final var testEndpoint = new TestEndpoint();
|
||||||
|
final var handlerContext = Mockito.mock(HandlerContext.class);
|
||||||
|
final var inputStream = Mockito.mock(InputStream.class);
|
||||||
|
|
||||||
|
when(handlerContext.getMessageBodySingleStream()).thenReturn(Optional.of(inputStream));
|
||||||
|
doThrow(new IOException("test")).when(this.objectMapper)
|
||||||
|
.readValue(inputStream, EndpointRequest.class);
|
||||||
|
|
||||||
|
final var result = testEndpoint.parseRequest(handlerContext);
|
||||||
|
assertNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testParseRequest_NoStream() throws Exception {
|
||||||
|
//noinspection resource
|
||||||
|
final var testEndpoint = new TestEndpoint();
|
||||||
|
final var handlerContext = Mockito.mock(HandlerContext.class);
|
||||||
|
|
||||||
|
when(handlerContext.getMessageBodySingleStream()).thenReturn(Optional.empty());
|
||||||
|
when(handlerContext.getMessageBodyMultiStream()).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
final var result = testEndpoint.parseRequest(handlerContext);
|
||||||
|
assertNull(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
+955
-6
@@ -1,25 +1,974 @@
|
|||||||
package com.cleverthis.authservice.service.impl;
|
package com.cleverthis.authservice.service.impl;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.cleverthis.authservice.config.KeycloakClientConfigProperties;
|
import com.cleverthis.authservice.config.KeycloakClientConfigProperties;
|
||||||
|
import com.cleverthis.authservice.dto.RegistrationRequest;
|
||||||
|
import com.cleverthis.authservice.dto.keycloakadmin.KeycloakInvitationRequest;
|
||||||
|
import com.cleverthis.authservice.exception.RegistrationException;
|
||||||
|
import com.cleverthis.authservice.exception.ServiceUnavailableException;
|
||||||
|
import com.cleverthis.authservice.exception.UserAlreadyExistsException;
|
||||||
|
import jakarta.ws.rs.ClientErrorException;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import jakarta.ws.rs.ProcessingException;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.keycloak.admin.client.Keycloak;
|
import org.keycloak.admin.client.Keycloak;
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
class KeycloakAdminReactiveClientTest {
|
class KeycloakAdminReactiveClientTest {
|
||||||
private KeycloakAdminReactiveClient adminClient;
|
private KeycloakAdminReactiveClient adminClient;
|
||||||
|
private final Keycloak keycloak = mock(Keycloak.class);
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
final var keycloak = Mockito.mock(Keycloak.class);
|
when(this.keycloak.realm("test-realm")).thenReturn(mock());
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").organizations()).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").groups()).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").users()).thenReturn(mock());
|
||||||
|
|
||||||
final var configProperties = KeycloakClientConfigProperties.builder()
|
final var configProperties = KeycloakClientConfigProperties.builder()
|
||||||
.keycloakAdminHost("http://example.com/")
|
|
||||||
.realm("test-realm")
|
.realm("test-realm")
|
||||||
.adminAccountUsername("admin")
|
|
||||||
.adminAccountPassword("admin-password")
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.adminClient = Mockito.spy(new KeycloakAdminReactiveClient(keycloak, configProperties));
|
this.adminClient =
|
||||||
|
Mockito.spy(new KeycloakAdminReactiveClient(this.keycloak, configProperties));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement tests
|
@Test
|
||||||
|
void testCreateOrganization_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var orgToCreate = new OrganizationRepresentation();
|
||||||
|
orgToCreate.setId("test-id");
|
||||||
|
orgToCreate.setName("Test Organization");
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily())
|
||||||
|
.thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
when(response.readEntity(OrganizationRepresentation.class)).thenReturn(orgToCreate);
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm")
|
||||||
|
.organizations().create(orgToCreate))
|
||||||
|
.thenReturn(response);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final var result = this.adminClient.createOrganization(orgToCreate).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals("Test Organization", result.getName());
|
||||||
|
//noinspection resource
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations()).create(orgToCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateOrganization_Failure() {
|
||||||
|
// Arrange
|
||||||
|
final var orgToCreate = new OrganizationRepresentation();
|
||||||
|
orgToCreate.setId("test-id");
|
||||||
|
orgToCreate.setName("Test Organization");
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
|
||||||
|
// keycloak return non-200
|
||||||
|
when(response.getStatusInfo().getFamily())
|
||||||
|
.thenReturn(Response.Status.Family.CLIENT_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Error occurred");
|
||||||
|
|
||||||
|
when(
|
||||||
|
this.keycloak.realm("test-realm").organizations()
|
||||||
|
.create(orgToCreate))
|
||||||
|
.thenReturn(response);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ServiceUnavailableException.class,
|
||||||
|
() -> this.adminClient.createOrganization(orgToCreate).block());
|
||||||
|
|
||||||
|
|
||||||
|
// client read exception
|
||||||
|
when(response.getStatusInfo().getFamily())
|
||||||
|
.thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
when(response.readEntity(OrganizationRepresentation.class))
|
||||||
|
.thenThrow(new ProcessingException("test"));
|
||||||
|
|
||||||
|
when(
|
||||||
|
this.keycloak.realm("test-realm").organizations()
|
||||||
|
.create(orgToCreate))
|
||||||
|
.thenReturn(response);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ProcessingException.class,
|
||||||
|
() -> this.adminClient.createOrganization(orgToCreate).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetOrganizations_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var organization1 = new OrganizationRepresentation();
|
||||||
|
organization1.setId("org-1");
|
||||||
|
organization1.setName("Organization 1");
|
||||||
|
|
||||||
|
final var organization2 = new OrganizationRepresentation();
|
||||||
|
organization2.setId("org-2");
|
||||||
|
organization2.setName("Organization 2");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm")
|
||||||
|
.organizations().list(null, Integer.MAX_VALUE))
|
||||||
|
.thenReturn(List.of(organization1, organization2));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final var result = this.adminClient.getOrganizations().block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
assertEquals("Organization 1", result.get(0).getName());
|
||||||
|
assertEquals("Organization 2", result.get(1).getName());
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations()).list(null, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetOrganizationById_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var organization = new OrganizationRepresentation();
|
||||||
|
organization.setId("org-1");
|
||||||
|
organization.setName("Test Organization");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm")
|
||||||
|
.organizations().get("org-1"))
|
||||||
|
.thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm")
|
||||||
|
.organizations().get("org-1").toRepresentation())
|
||||||
|
.thenReturn(organization);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final var result = this.adminClient.getOrganizationById("org-1").block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals("org-1", result.getId());
|
||||||
|
assertEquals("Test Organization", result.getName());
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations().get("org-1"))
|
||||||
|
.toRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetOrganizationById_NotFound() {
|
||||||
|
// Arrange
|
||||||
|
when(this.keycloak.realm("test-realm")
|
||||||
|
.organizations().get("non-existing-id"))
|
||||||
|
.thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm")
|
||||||
|
.organizations().get("non-existing-id").toRepresentation())
|
||||||
|
.thenThrow(new NotFoundException("Organization not found"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class,
|
||||||
|
() -> this.adminClient.getOrganizationById("non-existing-id").block());
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations().get("non-existing-id"))
|
||||||
|
.toRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateOrganization_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var orgToUpdate = new OrganizationRepresentation();
|
||||||
|
orgToUpdate.setId("org-1");
|
||||||
|
orgToUpdate.setName("Updated Organization");
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).update(orgToUpdate))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.updateOrganization(orgId, orgToUpdate).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
//noinspection resource
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations().get(orgId)).update(orgToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateOrganization_Failure_ClientError() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var orgToUpdate = new OrganizationRepresentation();
|
||||||
|
orgToUpdate.setId("org-1");
|
||||||
|
orgToUpdate.setName("Updated Organization");
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).update(orgToUpdate))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.CLIENT_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Client error occurred");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ServiceUnavailableException.class,
|
||||||
|
() -> this.adminClient.updateOrganization(orgId, orgToUpdate).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateOrganization_Failure_ProcessingException() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var orgToUpdate = new OrganizationRepresentation();
|
||||||
|
orgToUpdate.setId("org-1");
|
||||||
|
orgToUpdate.setName("Updated Organization");
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).update(orgToUpdate))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SERVER_ERROR);
|
||||||
|
when(response.readEntity(String.class))
|
||||||
|
.thenThrow(new ProcessingException("Processing error"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ProcessingException.class,
|
||||||
|
() -> this.adminClient.updateOrganization(orgId, orgToUpdate).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeleteOrganization_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).delete())
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.deleteOrganization(orgId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
//noinspection resource
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations().get(orgId)).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeleteOrganization_Failure_ClientError() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).delete())
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.CLIENT_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Client error occurred");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ServiceUnavailableException.class,
|
||||||
|
() -> this.adminClient.deleteOrganization(orgId).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeleteOrganization_Failure_ServerError() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).delete())
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SERVER_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Server error occurred");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ServiceUnavailableException.class,
|
||||||
|
() -> this.adminClient.deleteOrganization(orgId).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInviteUserToOrganization_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var invitation = new KeycloakInvitationRequest("test@example.com", "Test", "User");
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.inviteUser(invitation.getEmail(), invitation.getFirstName(),
|
||||||
|
invitation.getLastName()))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.inviteUserToOrganization(orgId, invitation).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
//noinspection resource
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations().get(orgId).members())
|
||||||
|
.inviteUser(invitation.getEmail(), invitation.getFirstName(),
|
||||||
|
invitation.getLastName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInviteUserToOrganization_Failure_ClientError() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var invitation = new KeycloakInvitationRequest("test@example.com", "Test", "User");
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.inviteUser(invitation.getEmail(), invitation.getFirstName(),
|
||||||
|
invitation.getLastName()))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.CLIENT_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Client error occurred");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ServiceUnavailableException.class,
|
||||||
|
() -> this.adminClient.inviteUserToOrganization(orgId, invitation).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInviteUserToOrganization_Failure_Exception() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var invitation = new KeycloakInvitationRequest("test@example.com", "Test", "User");
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.inviteUser(invitation.getEmail(), invitation.getFirstName(),
|
||||||
|
invitation.getLastName()))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SERVER_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenThrow(new RuntimeException("Unexpected error"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(RuntimeException.class,
|
||||||
|
() -> this.adminClient.inviteUserToOrganization(orgId, invitation).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetOrganizationMembers_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
|
||||||
|
final var member1 = new MemberRepresentation();
|
||||||
|
member1.setId("user-1");
|
||||||
|
member1.setUsername("member1");
|
||||||
|
|
||||||
|
final var member2 = new MemberRepresentation();
|
||||||
|
member2.setId("user-2");
|
||||||
|
member2.setUsername("member2");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId))
|
||||||
|
.thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members())
|
||||||
|
.thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)
|
||||||
|
.members().list(null, Integer.MAX_VALUE))
|
||||||
|
.thenReturn(List.of(member1, member2));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final var result = this.adminClient.getOrganizationMembers(orgId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
assertEquals("user-1", result.get(0).getId());
|
||||||
|
assertEquals("user-2", result.get(1).getId());
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations().get(orgId)
|
||||||
|
.members()).list(null, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetOrganizationMembers_Failure_NotFound() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "non-existing-org";
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId))
|
||||||
|
.thenThrow(new NotFoundException("Organization not found"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class,
|
||||||
|
() -> this.adminClient.getOrganizationMembers(orgId).block());
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations()).get(orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAddMemberToOrganization_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.addMember(userId))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.addMemberToOrganization(orgId, userId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
//noinspection resource
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations().get(orgId).members())
|
||||||
|
.addMember(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAddMemberToOrganization_Failure_ClientError() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.addMember(userId))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.CLIENT_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Client error occurred");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ServiceUnavailableException.class,
|
||||||
|
() -> this.adminClient.addMemberToOrganization(orgId, userId).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAddMemberToOrganization_Failure_UnexpectedException() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.addMember(userId))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SERVER_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenThrow(new RuntimeException("Unexpected error"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(RuntimeException.class,
|
||||||
|
() -> this.adminClient.addMemberToOrganization(orgId, userId).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRemoveMemberFromOrganization_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.removeMember(userId))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.removeMemberFromOrganization(orgId, userId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
//noinspection resource
|
||||||
|
verify(this.keycloak.realm("test-realm").organizations().get(orgId).members())
|
||||||
|
.removeMember(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRemoveMemberFromOrganization_Failure_ClientError() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.removeMember(userId))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.CLIENT_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Client error occurred");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ServiceUnavailableException.class,
|
||||||
|
() -> this.adminClient.removeMemberFromOrganization(orgId, userId).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRemoveMemberFromOrganization_Failure_Exception() {
|
||||||
|
// Arrange
|
||||||
|
final var orgId = "org-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()).thenReturn(
|
||||||
|
mock());
|
||||||
|
when(this.keycloak.realm("test-realm").organizations().get(orgId).members()
|
||||||
|
.removeMember(userId))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SERVER_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenThrow(new RuntimeException("Unexpected error"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(RuntimeException.class,
|
||||||
|
() -> this.adminClient.removeMemberFromOrganization(orgId, userId).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSubGroup_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var parentGroupId = "parent-group-1";
|
||||||
|
final var subGroup = new GroupRepresentation();
|
||||||
|
subGroup.setName("Sub Group 1");
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId).subGroup(subGroup))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.createSubGroup(parentGroupId, subGroup).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
//noinspection resource
|
||||||
|
verify(this.keycloak.realm("test-realm").groups().group(parentGroupId)).subGroup(subGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSubGroup_Failure_ClientError() {
|
||||||
|
// Arrange
|
||||||
|
final var parentGroupId = "parent-group-1";
|
||||||
|
final var subGroup = new GroupRepresentation();
|
||||||
|
subGroup.setName("Sub Group 1");
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId).subGroup(subGroup))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.CLIENT_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Client error occurred");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(ServiceUnavailableException.class,
|
||||||
|
() -> this.adminClient.createSubGroup(parentGroupId, subGroup).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSubGroup_Failure_Exception() {
|
||||||
|
// Arrange
|
||||||
|
final var parentGroupId = "parent-group-1";
|
||||||
|
final var subGroup = new GroupRepresentation();
|
||||||
|
subGroup.setName("Sub Group 1");
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId).subGroup(subGroup))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SERVER_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenThrow(new RuntimeException("Unexpected error"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(RuntimeException.class,
|
||||||
|
() -> this.adminClient.createSubGroup(parentGroupId, subGroup).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetGroupById_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "group-1";
|
||||||
|
final var group = new GroupRepresentation();
|
||||||
|
group.setId(groupId);
|
||||||
|
group.setName("Test Group");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId))
|
||||||
|
.thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId).toRepresentation())
|
||||||
|
.thenReturn(group);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final var result = this.adminClient.getGroupById(groupId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals("group-1", result.getId());
|
||||||
|
assertEquals("Test Group", result.getName());
|
||||||
|
verify(this.keycloak.realm("test-realm").groups().group(groupId)).toRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetGroupById_NotFound() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "non-existing-group";
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId))
|
||||||
|
.thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId).toRepresentation())
|
||||||
|
.thenThrow(new NotFoundException("Group not found"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class,
|
||||||
|
() -> this.adminClient.getGroupById(groupId).block());
|
||||||
|
verify(this.keycloak.realm("test-realm").groups().group(groupId)).toRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListSubGroups_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var parentGroupId = "parent-group-1";
|
||||||
|
|
||||||
|
final var subGroup1 = new GroupRepresentation();
|
||||||
|
subGroup1.setId("sub-group-1");
|
||||||
|
subGroup1.setName("Sub Group 1");
|
||||||
|
|
||||||
|
final var subGroup2 = new GroupRepresentation();
|
||||||
|
subGroup2.setId("sub-group-2");
|
||||||
|
subGroup2.setName("Sub Group 2");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId)
|
||||||
|
.getSubGroups(null, Integer.MAX_VALUE, false))
|
||||||
|
.thenReturn(List.of(subGroup1, subGroup2));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final var result = this.adminClient.listSubGroups(parentGroupId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
assertEquals("sub-group-1", result.get(0).getId());
|
||||||
|
assertEquals("sub-group-2", result.get(1).getId());
|
||||||
|
verify(this.keycloak.realm("test-realm").groups().group(parentGroupId))
|
||||||
|
.getSubGroups(null, Integer.MAX_VALUE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListSubGroups_NotFound() {
|
||||||
|
// Arrange
|
||||||
|
final var parentGroupId = "non-existing-group";
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(parentGroupId))
|
||||||
|
.thenThrow(new NotFoundException("Group not found"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class,
|
||||||
|
() -> this.adminClient.listSubGroups(parentGroupId).block());
|
||||||
|
verify(this.keycloak.realm("test-realm").groups()).group(parentGroupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateGroup_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "group-1";
|
||||||
|
final var groupToUpdate = new GroupRepresentation();
|
||||||
|
groupToUpdate.setId("group-1");
|
||||||
|
groupToUpdate.setName("Updated Group");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId)).thenReturn(mock());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.updateGroup(groupId, groupToUpdate).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify(this.keycloak.realm("test-realm").groups().group(groupId)).update(groupToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateGroup_NotFound() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "non-existing-group";
|
||||||
|
final var groupToUpdate = new GroupRepresentation();
|
||||||
|
groupToUpdate.setId("non-existing-group");
|
||||||
|
groupToUpdate.setName("Non-existing Group");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId))
|
||||||
|
.thenThrow(new NotFoundException("Group not found"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class,
|
||||||
|
() -> this.adminClient.updateGroup(groupId, groupToUpdate).block());
|
||||||
|
verify(this.keycloak.realm("test-realm").groups()).group(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeleteGroup_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "group-1";
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId)).thenReturn(mock());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.deleteGroup(groupId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify(this.keycloak.realm("test-realm").groups().group(groupId)).remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeleteGroup_NotFound() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "non-existing-group";
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId))
|
||||||
|
.thenThrow(new NotFoundException("Group not found"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class,
|
||||||
|
() -> this.adminClient.deleteGroup(groupId).block());
|
||||||
|
verify(this.keycloak.realm("test-realm").groups()).group(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAddMemberToGroup_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "group-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").users().get(userId)).thenReturn(mock());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.addMemberToGroup(groupId, userId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify(this.keycloak.realm("test-realm").users().get(userId)).joinGroup(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAddMemberToGroup_ClientError() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "group-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
final var userResource = mock(UserResource.class);
|
||||||
|
when(this.keycloak.realm("test-realm").users().get(userId)).thenReturn(userResource);
|
||||||
|
doThrow(new ClientErrorException(404)).when(userResource).joinGroup(groupId);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> this.adminClient.addMemberToGroup(groupId, userId).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetGroupMembers_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "group-1";
|
||||||
|
|
||||||
|
final var member1 = new UserRepresentation();
|
||||||
|
member1.setId("user-1");
|
||||||
|
member1.setUsername("member1");
|
||||||
|
|
||||||
|
final var member2 = new UserRepresentation();
|
||||||
|
member2.setId("user-2");
|
||||||
|
member2.setUsername("member2");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId).members())
|
||||||
|
.thenReturn(List.of(member1, member2));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final var result = this.adminClient.getGroupMembers(groupId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
assertEquals("user-1", result.get(0).getId());
|
||||||
|
assertEquals("user-2", result.get(1).getId());
|
||||||
|
verify(this.keycloak.realm("test-realm").groups().group(groupId)).members();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetGroupMembers_GroupNotFound() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "non-existing-group";
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").groups().group(groupId))
|
||||||
|
.thenThrow(new NotFoundException("Group not found"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class,
|
||||||
|
() -> this.adminClient.getGroupMembers(groupId).block());
|
||||||
|
verify(this.keycloak.realm("test-realm").groups()).group(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRemoveMemberFromGroup_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "group-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").users().get(userId)).thenReturn(mock());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.removeMemberFromGroup(groupId, userId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify(this.keycloak.realm("test-realm").users().get(userId)).leaveGroup(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRemoveMemberFromGroup_Failure_ClientError() {
|
||||||
|
// Arrange
|
||||||
|
final var groupId = "group-1";
|
||||||
|
final var userId = "user-1";
|
||||||
|
|
||||||
|
final var userResource = mock(UserResource.class);
|
||||||
|
when(this.keycloak.realm("test-realm").users().get(userId)).thenReturn(userResource);
|
||||||
|
doThrow(new ClientErrorException(404)).when(userResource).leaveGroup(groupId);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> this.adminClient.removeMemberFromGroup(groupId, userId).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRegisterUser_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var registrationRequest = new RegistrationRequest(
|
||||||
|
"John", "Doe", "john@example.com", "password123"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").users().create(any(UserRepresentation.class)))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SUCCESSFUL);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
this.adminClient.registerUser(registrationRequest).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
//noinspection resource
|
||||||
|
verify(this.keycloak.realm("test-realm").users())
|
||||||
|
.create(argThat(user -> user.getUsername().equals("john@example.com")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRegisterUser_UserAlreadyExists() {
|
||||||
|
// Arrange
|
||||||
|
final var registrationRequest = new RegistrationRequest(
|
||||||
|
"John", "Doe", "john@example.com", "password123"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").users().create(any(UserRepresentation.class)))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.CLIENT_ERROR);
|
||||||
|
when(response.getStatus()).thenReturn(409);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(UserAlreadyExistsException.class,
|
||||||
|
() -> this.adminClient.registerUser(registrationRequest).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRegisterUser_ServerError() {
|
||||||
|
// Arrange
|
||||||
|
final var registrationRequest = new RegistrationRequest(
|
||||||
|
"John", "Doe", "john@example.com", "password123"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var response = mock(Response.class);
|
||||||
|
when(this.keycloak.realm("test-realm").users().create(any(UserRepresentation.class)))
|
||||||
|
.thenReturn(response);
|
||||||
|
when(response.getStatusInfo()).thenReturn(mock());
|
||||||
|
when(response.getStatusInfo().getFamily()).thenReturn(Response.Status.Family.SERVER_ERROR);
|
||||||
|
when(response.readEntity(String.class)).thenReturn("Internal Server Error");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(RegistrationException.class,
|
||||||
|
() -> this.adminClient.registerUser(registrationRequest).block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetUserDetails_Success() {
|
||||||
|
// Arrange
|
||||||
|
final var userId = "user-1";
|
||||||
|
final var userRepresentation = new UserRepresentation();
|
||||||
|
userRepresentation.setId(userId);
|
||||||
|
userRepresentation.setUsername("user1");
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").users().get(userId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").users().get(userId).toRepresentation())
|
||||||
|
.thenReturn(userRepresentation);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final var result = this.adminClient.getUserDetails(userId).block();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals("user-1", result.getId());
|
||||||
|
assertEquals("user1", result.getUsername());
|
||||||
|
verify(this.keycloak.realm("test-realm").users().get(userId)).toRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetUserDetails_UserNotFound() {
|
||||||
|
// Arrange
|
||||||
|
final var userId = "non-existing-user";
|
||||||
|
|
||||||
|
when(this.keycloak.realm("test-realm").users().get(userId)).thenReturn(mock());
|
||||||
|
when(this.keycloak.realm("test-realm").users().get(userId).toRepresentation())
|
||||||
|
.thenThrow(new NotFoundException("User not found"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class,
|
||||||
|
() -> this.adminClient.getUserDetails(userId).block());
|
||||||
|
verify(this.keycloak.realm("test-realm").users().get(userId)).toRepresentation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user