more integration tests
This commit is contained in:
parent
15226ab4a2
commit
2652da80df
3 changed files with 679 additions and 2 deletions
|
@ -1,4 +1,4 @@
|
|||
Release:
|
||||
Master:
|
||||
|
||||
.. image:: https://travis-ci.com/SafeExamBrowser/seb-server.svg?branch=master
|
||||
:target: https://travis-ci.com/SafeExamBrowser/seb-server
|
||||
|
@ -12,7 +12,7 @@ Release:
|
|||
Development:
|
||||
|
||||
.. image:: https://travis-ci.com/SafeExamBrowser/seb-server.svg?branch=development
|
||||
:target: https://travis-ci.com/SafeExamBrowser/seb-server
|
||||
:target: https://github.com/SafeExamBrowser/seb-server/tree/development
|
||||
.. image:: https://codecov.io/gh/SafeExamBrowser/seb-server/branch/development/graph/badge.svg
|
||||
:target: https://codecov.io/gh/SafeExamBrowser/seb-server
|
||||
.. image:: https://img.shields.io/github/last-commit/SafeExamBrowser/seb-server/development?logo=github
|
||||
|
|
|
@ -0,0 +1,567 @@
|
|||
/*
|
||||
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.integration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler;
|
||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.RunningExamInfo;
|
||||
|
||||
public class SEBClientBot {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SEBClientBot.class);
|
||||
|
||||
private static final Character COMMA = ',';
|
||||
private static final Character AMPERSAND = '&';
|
||||
private static final Character EQUALITY_SIGN = '=';
|
||||
|
||||
private static final String LIST_SEPARATOR = COMMA.toString();
|
||||
private static final String FORM_URL_ENCODED_SEPARATOR = AMPERSAND.toString();
|
||||
private static final String FORM_URL_ENCODED_NAME_VALUE_SEPARATOR = EQUALITY_SIGN.toString();
|
||||
|
||||
private static final String PARAM_INSTITUTION_ID = "institutionId";
|
||||
private static final String EXAM_API_PARAM_EXAM_ID = "examId";
|
||||
private static final String EXAM_API_SEB_CONNECTION_TOKEN = "SEBConnectionToken";
|
||||
private static final String EXAM_API_USER_SESSION_ID = "seb_user_session_id";
|
||||
private static final String EXAM_API_HANDSHAKE_ENDPOINT = "/handshake";
|
||||
private static final String EXAM_API_CONFIGURATION_REQUEST_ENDPOINT = "/examconfig";
|
||||
private static final String EXAM_API_PING_ENDPOINT = "/sebping";
|
||||
private static final String EXAM_API_PING_TIMESTAMP = "timestamp";
|
||||
private static final String EXAM_API_PING_NUMBER = "ping-number";
|
||||
private static final String EXAM_API_EVENT_ENDPOINT = "/seblog";
|
||||
|
||||
private static final long ONE_SECOND = 1000; // milliseconds
|
||||
static final long TEN_SECONDS = 10 * ONE_SECOND;
|
||||
static final long ONE_MINUTE = 60 * ONE_SECOND;
|
||||
@SuppressWarnings("unused")
|
||||
private static final long ONE_HOUR = 60 * ONE_MINUTE;
|
||||
|
||||
//private final ExecutorService executorService;
|
||||
private final List<String> scopes = Arrays.asList("read", "write");
|
||||
private final ObjectMapper jsonMapper = new ObjectMapper();
|
||||
private final Random random = new Random();
|
||||
|
||||
String webserviceAddress = "http://localhost:8080";
|
||||
String accessTokenEndpoint = "/oauth/token";
|
||||
String clientId = "test";
|
||||
String sessionId = null;
|
||||
String clientSecret = "test";
|
||||
String apiPath = "/exam-api";
|
||||
String apiVersion = "v1";
|
||||
String examId = "2";
|
||||
String institutionId = "1";
|
||||
int numberOfConnections = 4;
|
||||
long establishDelay = 0;
|
||||
long pingInterval = 100;
|
||||
long pingPause = 0;
|
||||
long pingPauseDelay = 0;
|
||||
long errorInterval = ONE_SECOND;
|
||||
long warnInterval = ONE_SECOND / 2;
|
||||
long runtime = ONE_SECOND * 2;
|
||||
int connectionAttempts = 1;
|
||||
|
||||
public SEBClientBot(final ClientCredentials credentials, final String examId, final String instId)
|
||||
throws Exception {
|
||||
|
||||
this.clientId = credentials.clientIdAsString();
|
||||
this.clientSecret = credentials.secretAsString();
|
||||
this.examId = examId;
|
||||
this.institutionId = instId;
|
||||
|
||||
//this.executorService = Executors.newFixedThreadPool(this.numberOfConnections);
|
||||
|
||||
for (int i = 0; i < this.numberOfConnections; i++) {
|
||||
final String sessionId = StringUtils.isNotBlank(this.sessionId)
|
||||
? this.sessionId
|
||||
: "connection_" + getRandomName();
|
||||
|
||||
new ConnectionBot(sessionId).run();
|
||||
//this.executorService.execute(new ConnectionBot(sessionId));
|
||||
}
|
||||
|
||||
//this.executorService.shutdown();
|
||||
}
|
||||
|
||||
private String getRandomName() {
|
||||
final StringBuilder sb = new StringBuilder(String.valueOf(this.random.nextInt(100)));
|
||||
while (sb.length() < 3) {
|
||||
sb.insert(0, "0");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private final class ConnectionBot implements Runnable {
|
||||
|
||||
private final String name;
|
||||
private final OAuth2RestTemplate restTemplate;
|
||||
|
||||
private final String handshakeURI = SEBClientBot.this.webserviceAddress +
|
||||
SEBClientBot.this.apiPath + "/" +
|
||||
SEBClientBot.this.apiVersion + EXAM_API_HANDSHAKE_ENDPOINT;
|
||||
private final String configurartionURI = SEBClientBot.this.webserviceAddress +
|
||||
SEBClientBot.this.apiPath + "/" +
|
||||
SEBClientBot.this.apiVersion + EXAM_API_CONFIGURATION_REQUEST_ENDPOINT;
|
||||
private final String pingURI = SEBClientBot.this.webserviceAddress +
|
||||
SEBClientBot.this.apiPath + "/" +
|
||||
SEBClientBot.this.apiVersion + EXAM_API_PING_ENDPOINT;
|
||||
private final String eventURI = SEBClientBot.this.webserviceAddress +
|
||||
SEBClientBot.this.apiPath + "/" +
|
||||
SEBClientBot.this.apiVersion + EXAM_API_EVENT_ENDPOINT;
|
||||
|
||||
private final HttpEntity<?> connectBody;
|
||||
|
||||
protected ConnectionBot(final String name) {
|
||||
this.name = name;
|
||||
this.restTemplate = createRestTemplate(null);
|
||||
final MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||
this.connectBody = new HttpEntity<>(PARAM_INSTITUTION_ID +
|
||||
FORM_URL_ENCODED_NAME_VALUE_SEPARATOR +
|
||||
SEBClientBot.this.institutionId
|
||||
// + Constants.FORM_URL_ENCODED_SEPARATOR
|
||||
// + API.EXAM_API_PARAM_EXAM_ID
|
||||
// + Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR
|
||||
// + this.examId
|
||||
,
|
||||
headers);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
int attempt = 0;
|
||||
String connectionToken = null;
|
||||
|
||||
while (connectionToken == null && attempt < SEBClientBot.this.connectionAttempts) {
|
||||
attempt++;
|
||||
log.info("ConnectionBot {} : Try to request access-token; attempt: {}", this.name, attempt);
|
||||
try {
|
||||
|
||||
final OAuth2AccessToken accessToken = this.restTemplate.getAccessToken();
|
||||
log.info("ConnectionBot {} : Got access token: {}", this.name, accessToken);
|
||||
connectionToken = createConnection();
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("ConnectionBot {} : Failed to request access-token: ", this.name, e);
|
||||
if (attempt >= SEBClientBot.this.connectionAttempts) {
|
||||
log.error("ConnectionBot {} : Gave up afer {} connection attempts: ", this.name, attempt);
|
||||
throw new RuntimeException("Connection Error. See Logs", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||
headers.set(EXAM_API_SEB_CONNECTION_TOKEN, connectionToken);
|
||||
|
||||
final MultiValueMap<String, String> eventHeaders = new LinkedMultiValueMap<>();
|
||||
eventHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
eventHeaders.set(EXAM_API_SEB_CONNECTION_TOKEN, connectionToken);
|
||||
|
||||
if (connectionToken != null) {
|
||||
if (getConfig(headers) && establishConnection(headers)) {
|
||||
|
||||
final PingEntity pingHeader = new PingEntity(headers);
|
||||
final EventEntity errorHeader = new EventEntity(eventHeaders, "ERROR_LOG");
|
||||
final EventEntity warnHeader = new EventEntity(eventHeaders, "WARN_LOG");
|
||||
|
||||
try {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long endTime = startTime + SEBClientBot.this.runtime;
|
||||
final long pingPauseStart = startTime + SEBClientBot.this.pingPauseDelay;
|
||||
final long pingPauseEnd = pingPauseStart + SEBClientBot.this.pingPause;
|
||||
long currentTime = startTime;
|
||||
long lastPingTime = startTime;
|
||||
long lastErrorTime = startTime;
|
||||
long lastWarnTime = startTime;
|
||||
|
||||
while (currentTime < endTime) {
|
||||
if (currentTime - lastPingTime >= SEBClientBot.this.pingInterval &&
|
||||
!(currentTime > pingPauseStart && currentTime < pingPauseEnd)) {
|
||||
|
||||
pingHeader.next();
|
||||
if (!sendPing(pingHeader)) {
|
||||
// expecting a quit instruction was sent here
|
||||
return;
|
||||
}
|
||||
lastPingTime = currentTime;
|
||||
}
|
||||
if (currentTime - lastErrorTime >= SEBClientBot.this.errorInterval) {
|
||||
errorHeader.next();
|
||||
sendEvent(errorHeader);
|
||||
lastErrorTime = currentTime;
|
||||
}
|
||||
if (currentTime - lastWarnTime >= SEBClientBot.this.warnInterval) {
|
||||
warnHeader.next();
|
||||
sendEvent(warnHeader);
|
||||
lastWarnTime = currentTime;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (final Exception e) {
|
||||
}
|
||||
currentTime = System.currentTimeMillis();
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
log.error("ConnectionBot {} : Error sending events: ", this.name, t);
|
||||
throw new RuntimeException("ConnectionBot {} : Error sending events: ");
|
||||
} finally {
|
||||
disconnect(connectionToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String createConnection() {
|
||||
log.info("ConnectionBot {} : init connection", this.name);
|
||||
|
||||
try {
|
||||
final ResponseEntity<String> exchange = this.restTemplate.exchange(
|
||||
this.handshakeURI,
|
||||
HttpMethod.POST,
|
||||
this.connectBody,
|
||||
new ParameterizedTypeReference<String>() {
|
||||
});
|
||||
|
||||
final HttpStatus statusCode = exchange.getStatusCode();
|
||||
if (statusCode.isError()) {
|
||||
throw new RuntimeException("Webservice answered with error: " + exchange.getBody());
|
||||
}
|
||||
|
||||
final Collection<RunningExamInfo> body = SEBClientBot.this.jsonMapper.readValue(
|
||||
exchange.getBody(),
|
||||
new TypeReference<Collection<RunningExamInfo>>() {
|
||||
});
|
||||
final String token = exchange.getHeaders().getFirst(EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
|
||||
log.info("ConnectionBot {} : successfully created connection, token: {} body: {} ",
|
||||
this.name,
|
||||
token,
|
||||
body);
|
||||
|
||||
return token;
|
||||
} catch (final Exception e) {
|
||||
log.error("ConnectionBot {} : Failed to init connection", this.name, e);
|
||||
throw new RuntimeException("ConnectionBot {} : Failed to init connection: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getConfig(final MultiValueMap<String, String> headers) {
|
||||
final HttpEntity<?> configHeader = new HttpEntity<>(headers);
|
||||
|
||||
log.info("ConnectionBot {} : get SEB Configuration", this.name);
|
||||
|
||||
try {
|
||||
final ResponseEntity<byte[]> exchange = this.restTemplate.exchange(
|
||||
this.configurartionURI + "?" + EXAM_API_PARAM_EXAM_ID +
|
||||
FORM_URL_ENCODED_NAME_VALUE_SEPARATOR +
|
||||
SEBClientBot.this.examId,
|
||||
HttpMethod.GET,
|
||||
configHeader,
|
||||
new ParameterizedTypeReference<byte[]>() {
|
||||
});
|
||||
|
||||
final HttpStatus statusCode = exchange.getStatusCode();
|
||||
if (statusCode.isError()) {
|
||||
throw new RuntimeException("Webservice answered with error: " + exchange.getBody());
|
||||
}
|
||||
|
||||
final byte[] config = exchange.getBody();
|
||||
|
||||
if (ArrayUtils.isEmpty(config)) {
|
||||
log.error("No Exam config get from API. processing anyway");
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(
|
||||
"ConnectionBot {} : successfully requested exam config: " + SEBClientBot.toString(config),
|
||||
this.name);
|
||||
} else {
|
||||
log.info("ConnectionBot {} : successfully requested exam config", this.name);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (final Exception e) {
|
||||
log.error("ConnectionBot {} : Failed get SEB Configuration", this.name, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean establishConnection(final MultiValueMap<String, String> headers) {
|
||||
|
||||
if (SEBClientBot.this.establishDelay > 0) {
|
||||
try {
|
||||
|
||||
log.info("Wait for connection activation -> {}", SEBClientBot.this.establishDelay);
|
||||
|
||||
Thread.sleep(SEBClientBot.this.establishDelay);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to wait for connection activiation -> {} : {}",
|
||||
SEBClientBot.this.establishDelay,
|
||||
e.getMessage());
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
final HttpEntity<?> configHeader = new HttpEntity<>(
|
||||
EXAM_API_USER_SESSION_ID +
|
||||
FORM_URL_ENCODED_NAME_VALUE_SEPARATOR +
|
||||
this.name,
|
||||
headers);
|
||||
|
||||
log.info("ConnectionBot {} : Trying to establish SEB client connection", this.name);
|
||||
|
||||
try {
|
||||
|
||||
final ResponseEntity<String> exchange = this.restTemplate.exchange(
|
||||
this.handshakeURI,
|
||||
HttpMethod.PUT,
|
||||
configHeader,
|
||||
new ParameterizedTypeReference<String>() {
|
||||
});
|
||||
|
||||
final HttpStatus statusCode = exchange.getStatusCode();
|
||||
if (statusCode.isError()) {
|
||||
throw new RuntimeException("Webservice answered with error: " + exchange.getBody());
|
||||
}
|
||||
|
||||
log.info("ConnectionBot {} : successfully established SEB client connection", this.name);
|
||||
|
||||
return true;
|
||||
} catch (final Exception e) {
|
||||
log.error("ConnectionBot {} : Failed get established SEB client connection", this.name, e);
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sendPing(final HttpEntity<String> pingHeader) {
|
||||
try {
|
||||
|
||||
final ResponseEntity<String> exchange = this.restTemplate.exchange(
|
||||
this.pingURI,
|
||||
HttpMethod.POST,
|
||||
pingHeader,
|
||||
new ParameterizedTypeReference<String>() {
|
||||
});
|
||||
|
||||
if (exchange.hasBody() && exchange.getBody().contains("SEB_QUIT")) {
|
||||
log.info("SEB_QUIT client {}, response: {}",
|
||||
pingHeader.getHeaders().get(EXAM_API_SEB_CONNECTION_TOKEN),
|
||||
exchange.getBody());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (final Exception e) {
|
||||
log.error("ConnectionBot {} : Failed send ping", this.name, e);
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sendEvent(final HttpEntity<String> eventHeader) {
|
||||
try {
|
||||
|
||||
this.restTemplate.exchange(
|
||||
this.eventURI,
|
||||
HttpMethod.POST,
|
||||
eventHeader,
|
||||
new ParameterizedTypeReference<String>() {
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (final Exception e) {
|
||||
log.error("ConnectionBot {} : Failed send event", this.name, e);
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean disconnect(final String connectionToken) {
|
||||
final MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||
headers.set(EXAM_API_SEB_CONNECTION_TOKEN, connectionToken);
|
||||
final HttpEntity<?> configHeader = new HttpEntity<>(headers);
|
||||
|
||||
log.info("ConnectionBot {} : Trying to delete SEB client connection", this.name);
|
||||
|
||||
try {
|
||||
|
||||
final ResponseEntity<String> exchange = this.restTemplate.exchange(
|
||||
this.handshakeURI,
|
||||
HttpMethod.DELETE,
|
||||
configHeader,
|
||||
new ParameterizedTypeReference<String>() {
|
||||
});
|
||||
|
||||
final HttpStatus statusCode = exchange.getStatusCode();
|
||||
if (statusCode.isError()) {
|
||||
throw new RuntimeException("Webservice answered with error: " + exchange.getBody());
|
||||
}
|
||||
|
||||
log.info("ConnectionBot {} : successfully deleted SEB client connection", this.name);
|
||||
|
||||
return true;
|
||||
} catch (final Exception e) {
|
||||
log.error("ConnectionBot {} : Failed get deleted SEB client connection", this.name, e);
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OAuth2RestTemplate createRestTemplate(final String scopes) {
|
||||
final ClientCredentialsResourceDetails clientCredentialsResourceDetails =
|
||||
new ClientCredentialsResourceDetails();
|
||||
clientCredentialsResourceDetails
|
||||
.setAccessTokenUri(this.webserviceAddress + this.accessTokenEndpoint);
|
||||
clientCredentialsResourceDetails.setClientId(this.clientId);
|
||||
clientCredentialsResourceDetails.setClientSecret(this.clientSecret);
|
||||
if (StringUtils.isBlank(scopes)) {
|
||||
clientCredentialsResourceDetails.setScope(this.scopes);
|
||||
} else {
|
||||
clientCredentialsResourceDetails.setScope(
|
||||
Arrays.asList(StringUtils.split(scopes, LIST_SEPARATOR)));
|
||||
}
|
||||
|
||||
final OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(clientCredentialsResourceDetails);
|
||||
restTemplate.setErrorHandler(new OAuth2ErrorHandler(clientCredentialsResourceDetails) {
|
||||
|
||||
@Override
|
||||
public void handleError(final ClientHttpResponse response) throws IOException {
|
||||
System.out.println("********************** handleError: " + response.getStatusCode());
|
||||
super.handleError(response);
|
||||
}
|
||||
|
||||
});
|
||||
restTemplate
|
||||
.getMessageConverters()
|
||||
.add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
|
||||
//restTemplate.setRetryBadAccessTokens(true);
|
||||
|
||||
final SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
|
||||
simpleClientHttpRequestFactory.setReadTimeout(30000);
|
||||
simpleClientHttpRequestFactory.setOutputStreaming(false);
|
||||
restTemplate.setRequestFactory(simpleClientHttpRequestFactory);
|
||||
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
private static class PingEntity extends HttpEntity<String> {
|
||||
private final String pingBodyTemplate = EXAM_API_PING_TIMESTAMP +
|
||||
FORM_URL_ENCODED_NAME_VALUE_SEPARATOR +
|
||||
"%s" +
|
||||
FORM_URL_ENCODED_SEPARATOR +
|
||||
EXAM_API_PING_NUMBER +
|
||||
FORM_URL_ENCODED_NAME_VALUE_SEPARATOR +
|
||||
"%s";
|
||||
|
||||
private long timestamp = 0;
|
||||
private int count = 0;
|
||||
|
||||
protected PingEntity(final MultiValueMap<String, String> headers) {
|
||||
super(headers);
|
||||
}
|
||||
|
||||
void next() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
this.count++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBody() {
|
||||
return String.format(this.pingBodyTemplate, this.timestamp, this.count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBody() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class EventEntity extends HttpEntity<String> {
|
||||
private final String eventBodyTemplate =
|
||||
"{ \"type\": \"%s\", \"timestamp\": %s, \"text\": \"some error " + UUID.randomUUID() + " \" }";
|
||||
|
||||
private long timestamp = 0;
|
||||
private final String eventType;
|
||||
|
||||
protected EventEntity(final MultiValueMap<String, String> headers, final String eventType) {
|
||||
super(headers);
|
||||
this.eventType = eventType;
|
||||
}
|
||||
|
||||
void next() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBody() {
|
||||
return String.format(this.eventBodyTemplate, this.eventType, this.timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBody() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static CharBuffer toCharBuffer(final ByteBuffer byteBuffer) {
|
||||
if (byteBuffer == null) {
|
||||
return CharBuffer.allocate(0);
|
||||
}
|
||||
|
||||
byteBuffer.rewind();
|
||||
return StandardCharsets.UTF_8.decode(byteBuffer);
|
||||
}
|
||||
|
||||
public static String toString(final ByteBuffer byteBuffer) {
|
||||
return toCharBuffer(byteBuffer).toString();
|
||||
}
|
||||
|
||||
public static String toString(final byte[] byteArray) {
|
||||
if (byteArray == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return toString(ByteBuffer.wrap(byteArray));
|
||||
}
|
||||
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.junit.Before;
|
|||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
@ -42,6 +43,7 @@ import ch.ethz.seb.sebserver.gbl.Constants;
|
|||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.SEB_CLIENT_CONFIGURATION;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
||||
|
@ -73,6 +75,9 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
|
|||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
|
@ -108,6 +113,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSe
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.NewLmsSetup;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.SaveLmsSetup;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.ImportAsExam;
|
||||
|
@ -149,12 +155,15 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.Sa
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigHistory;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValues;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionDataList;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetRunningExamPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ActivateUserAccount;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ChangePassword;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountNames;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.NewUserAccount;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.SaveUserAccount;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
||||
|
@ -2017,4 +2026,105 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
|||
// xmlString);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private SEBClientConfigDAO sebClientConfigDAO;
|
||||
|
||||
@Test
|
||||
@Order(17)
|
||||
// *************************************
|
||||
// Use Case 16: Login as examSupport2 and get running exam with data
|
||||
// - Get list of running exams
|
||||
// - Simulate a SEB connection
|
||||
// - Join running exam by get the data for all SEB connections and for a single SEB connection.
|
||||
public void testUsecase17_RunningExam() throws IOException {
|
||||
final RestServiceImpl restService = createRestServiceForUser(
|
||||
"examSupport2",
|
||||
"examSupport2",
|
||||
new GetRunningExamPage(),
|
||||
new GetClientConnectionDataList(),
|
||||
new GetExtendedClientEventPage());
|
||||
|
||||
final RestServiceImpl adminRestService = createRestServiceForUser(
|
||||
"TestInstAdmin",
|
||||
"987654321",
|
||||
new NewClientConfig(),
|
||||
new ActivateClientConfig(),
|
||||
new GetClientConfigPage());
|
||||
|
||||
// get running exams
|
||||
final Result<Page<Exam>> runningExamsCall = restService.getBuilder(GetRunningExamPage.class)
|
||||
.call();
|
||||
|
||||
assertNotNull(runningExamsCall);
|
||||
assertFalse(runningExamsCall.hasError());
|
||||
final Page<Exam> page = runningExamsCall.get();
|
||||
assertFalse(page.content.isEmpty());
|
||||
final Exam exam = page.content.get(0);
|
||||
assertEquals("Demo Quiz 1 (MOCKUP)", exam.name);
|
||||
|
||||
// get SEB connections
|
||||
Result<Collection<ClientConnectionData>> connectionsCall =
|
||||
restService.getBuilder(GetClientConnectionDataList.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, exam.getModelId())
|
||||
.call();
|
||||
|
||||
assertNotNull(connectionsCall);
|
||||
assertFalse(connectionsCall.hasError());
|
||||
Collection<ClientConnectionData> connections = connectionsCall.get();
|
||||
// no SEB connections available yet
|
||||
assertTrue(connections.isEmpty());
|
||||
|
||||
// get active client config's credentials
|
||||
final Result<Page<SEBClientConfig>> cconfigs = adminRestService.getBuilder(GetClientConfigPage.class)
|
||||
.call();
|
||||
assertNotNull(cconfigs);
|
||||
assertFalse(cconfigs.hasError());
|
||||
final Page<SEBClientConfig> ccPage = cconfigs.get();
|
||||
assertFalse(ccPage.content.isEmpty());
|
||||
|
||||
final SEBClientConfig clientConfig = ccPage.content.get(0);
|
||||
assertTrue(clientConfig.isActive());
|
||||
final ClientCredentials credentials = this.sebClientConfigDAO.getSEBClientCredentials(clientConfig.getModelId())
|
||||
.getOrThrow();
|
||||
|
||||
adminRestService.getBuilder(ActivateClientConfig.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, clientConfig.getModelId())
|
||||
.call();
|
||||
|
||||
// simulate a SEB connection
|
||||
try {
|
||||
new SEBClientBot(credentials, exam.getModelId(), String.valueOf(exam.institutionId));
|
||||
Thread.sleep(2000);
|
||||
} catch (final Exception e) {
|
||||
fail(e.getCause().getMessage());
|
||||
}
|
||||
|
||||
connectionsCall =
|
||||
restService.getBuilder(GetClientConnectionDataList.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, exam.getModelId())
|
||||
.call();
|
||||
|
||||
assertNotNull(connectionsCall);
|
||||
assertFalse(connectionsCall.hasError());
|
||||
connections = connectionsCall.get();
|
||||
assertFalse(connections.isEmpty());
|
||||
final ClientConnectionData conData = connections.iterator().next();
|
||||
assertNotNull(conData);
|
||||
assertEquals(exam.id, conData.clientConnection.examId);
|
||||
assertFalse(conData.indicatorValues.isEmpty());
|
||||
final IndicatorValue indicatorValue = conData.indicatorValues.get(0);
|
||||
assertEquals("LAST_PING", indicatorValue.getType().name);
|
||||
|
||||
// get client logs
|
||||
final Result<Page<ExtendedClientEvent>> clientLogPage = restService.getBuilder(GetExtendedClientEventPage.class)
|
||||
.call();
|
||||
|
||||
assertNotNull(clientLogPage);
|
||||
assertFalse(clientLogPage.hasError());
|
||||
final Page<ExtendedClientEvent> clientLogs = clientLogPage.get();
|
||||
assertFalse(clientLogs.isEmpty());
|
||||
final ExtendedClientEvent extendedClientEvent = clientLogs.content.get(0);
|
||||
assertNotNull(extendedClientEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue