Skip to content

Commit

Permalink
feat(api-test): add JUnit5 extension for RandomUser IQSS#8251
Browse files Browse the repository at this point in the history
Many API tests require the use of a random user, which is no SuT
(subject under test). To gather such a user and ensure its deletion
from a target Dataverse instance after a test, this extension
provides a convenient way. Simple use ExtendWith and provide a
RandomUser parameter, you're done.
  • Loading branch information
poikilotherm committed Nov 30, 2021
1 parent e34d71e commit 4b3a941
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 0 deletions.
55 changes: 55 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/api/helpers/RandomUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package edu.harvard.iq.dataverse.api.helpers;

import com.jayway.restassured.path.json.JsonPath;
import com.jayway.restassured.response.Response;

import java.util.Map;

/**
* A class to represent a random user, retrieved from a Dataverse instance via its API
*
* There are some often used detail fields accessible via methods added for convenience.
* All fields from the JSON response may be accessed, too.
*/
public class RandomUser {

private static final String statusPath = "status";
private static final String dataPath = "data";
private static final String apiTokenPath = dataPath+".apiToken";
private static final String usernamePath = dataPath+".user.userName";
private static final String authUserPath = dataPath+".authenticatedUser";
private static final String persistentUserIdPath = authUserPath+".persistentUserId";

private final JsonPath json;

/**
* Package-private on intention - should only be created by {@link RandomUserExtension}
*/
RandomUser(Response response) {
this.json = response.jsonPath();
}

public String getStatus() {
return json.get(statusPath);
}

public String getApiToken() {
return json.get(apiTokenPath);
}

public String getUsername() {
return json.get(usernamePath);
}

public Map<String, String> getAuthenticatedUser() {
return json.get(authUserPath);
}

public String getPersistentUserId() {
return json.get(persistentUserIdPath);
}

public String toString() {
return "RandomUser["+json.getJsonObject("").toString()+"]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package edu.harvard.iq.dataverse.api.helpers;

import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.response.Response;
import edu.harvard.iq.dataverse.api.UtilIT;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.logging.Logger;

import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

/**
* A JUnit5 extension to resolve a parameter of a test method and inject a {@link RandomUser} into it.
* This user may be used to create/use other resources and subjects under test.
*/
public class RandomUserExtension implements ParameterResolver, AfterTestExecutionCallback {

private static final Logger logger = Logger.getLogger(RandomUserExtension.class.getCanonicalName());

private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass(), context.getRequiredTestClass(), context.getRequiredTestMethod()));
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType().equals(RandomUser.class);
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
Store store = getStore(extensionContext);
String parameterName = parameterContext.getParameter().getName();

RandomUser user = getNewRandomUser();
store.put(parameterName, user);
return user;
}

/**
* Create a new RandomUser via Dataverse API
* Hint: package-private on purpose to allow reuse within other of our extensions
*
* @return a new random user (or nothing, when assumptions aren't met)
*/
static RandomUser getNewRandomUser() {
// request user via API
Response createUser = UtilIT.createRandomUser();

// assume created or abort
assumeTrue(ContentType.fromContentType(createUser.contentType()) == ContentType.JSON,
() -> "Return data for was no JSON: "+createUser.print());
assumeTrue(createUser.getStatusCode() == HttpServletResponse.SC_OK,
() -> "User creation failed with status "+createUser.getStatusCode()+": "+createUser.print());

// store response as user
RandomUser user = new RandomUser(createUser);

// verify user
assumeTrue(user.getUsername() != null, () -> "Could not find a username for successfully created user.");
assumeFalse(user.getUsername().isEmpty(), () -> "Could not find a non-empty username for successfully created user.");
assumeFalse(user.getUsername().isBlank(), () -> "Could not find a non-blank username for successfully created user.");
assumeTrue(user.getApiToken() != null, () -> "Could not find an api token for successfully created user.");
assumeFalse(user.getApiToken().isEmpty(), () -> "Could not find a non-empty api token for successfully created user.");
assumeFalse(user.getApiToken().isBlank(), () -> "Could not find a non-blank api token for successfully created user.");

return user;
}

@Override
public void afterTestExecution(ExtensionContext extensionContext) throws Exception {
Store store = getStore(extensionContext);

// iterate all parameters
for (Parameter p : extensionContext.getRequiredTestMethod().getParameters()) {
// if type matches ...
if (p.getType() == RandomUser.class) {
// ... retrieve user from store
RandomUser user = store.get(p.getName(), RandomUser.class);
// and delete if present
if (user != null) {
deleteRandomUser(user);
} else {
// if not present, log an info, as this seems not right (but might have happened when creation failed)
logger.info("Could not find a previously stored RandomUser for parameter "+p.getName());
}
}
}
}

/**
* Delete a RandomUser via Dataverse API
* Hint: package-private on purpose to allow reuse within other of our extensions
*
* @param user A random user created via the Dataverse API before
*/
static void deleteRandomUser(RandomUser user) {
// delete via API
logger.info("Deleting user "+user.getUsername());
Response deleteUser = UtilIT.deleteUser(user.getUsername());

// if this does not work, log a warning, but continue (try to delete the others)
if (deleteUser.getStatusCode() != HttpServletResponse.SC_OK) {
logger.warning("Could not delete user "+user.getUsername()+": "+deleteUser.print());
}
}
}

0 comments on commit 4b3a941

Please sign in to comment.