diff --git a/codecov.yml b/codecov.yml index 7f5dad09..818b3cf4 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,7 +1,7 @@ coverage: precision: 2 round: down - range: "40...100" + range: "30..70" status: project: default: diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index 2f1bd980..18f1ed69 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -248,6 +248,9 @@ public final class Utils { } public static String formatDate(final DateTime dateTime) { + if (dateTime == null) { + return Constants.EMPTY_NOTE; + } return dateTime.toString(Constants.STANDARD_DATE_TIME_MILLIS_FORMATTER); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchActionPage.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchActionPage.java new file mode 100644 index 00000000..8d156ff6 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchActionPage.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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.service.remote.webservice.api.batch; + +import org.springframework.context.annotation.Lazy; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetBatchActionPage extends RestCall> { + + public GetBatchActionPage() { + super(new TypeKey<>( + CallType.GET_PAGE, + EntityType.BATCH_ACTION, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.BATCH_ACTION_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java index 85ce64fe..8000a979 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java @@ -156,7 +156,7 @@ public class FilterMap extends POSTMapper { } public String getConfigAttributeType() { - return getSQLWildcard(ConfigurationAttribute.FILTER_ATTR_TYPE); + return getString(ConfigurationAttribute.FILTER_ATTR_TYPE); } public Long getConfigValueConfigId() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java index cc3b6697..a7a6964e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java @@ -177,7 +177,7 @@ public class ConfigurationAttributeDAOImpl implements ConfigurationAttributeDAO final ConfigurationAttributeRecord newRecord = new ConfigurationAttributeRecord( data.id, data.name, - data.type.name(), + data.type != null ? data.type.name() : null, data.parentId, data.resources, data.validator, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedIndicatorValueService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedIndicatorValueService.java index 48519627..8e564645 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedIndicatorValueService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedIndicatorValueService.java @@ -154,11 +154,18 @@ public class DistributedIndicatorValueService implements DisposableBean { final Long recId = this.clientIndicatorValueMapper.indicatorRecordIdByConnectionId( connectionId, type); + if (recId != null) { - log.debug("Distributed indicator value cache already exists for: {}, {}", connectionId, type); + if (log.isTraceEnabled()) { + log.trace("Distributed indicator value cache already exists for: {}, {}", connectionId, type); + } return recId; } + if (log.isDebugEnabled()) { + log.info("Missing distributed indicator value cache. Create for: {}, {}", connectionId, type); + } + // if not, create new one and return PK final ClientIndicatorRecord clientEventRecord = new ClientIndicatorRecord( null, connectionId, type.id, initValue); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java index ab27ce25..34ca5f18 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java @@ -60,6 +60,7 @@ import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; @@ -107,19 +108,22 @@ public class JitsiProctoringService implements ExamProctoringService { private final Cryptor cryptor; private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; private final JSONMapper jsonMapper; + private final WebserviceInfo webserviceInfo; protected JitsiProctoringService( final AuthorizationService authorizationService, final ExamSessionService examSessionService, final Cryptor cryptor, final ClientHttpRequestFactoryService clientHttpRequestFactoryService, - final JSONMapper jsonMapper) { + final JSONMapper jsonMapper, + final WebserviceInfo webserviceInfo) { this.authorizationService = authorizationService; this.examSessionService = examSessionService; this.cryptor = cryptor; this.clientHttpRequestFactoryService = clientHttpRequestFactoryService; this.jsonMapper = jsonMapper; + this.webserviceInfo = webserviceInfo; } @Override @@ -141,6 +145,11 @@ public class JitsiProctoringService implements ExamProctoringService { "proctoringSettings:serverURL:invalidURL"); } + // In testing we do not check the JITSI service on URL to be able to test without service + if (this.webserviceInfo != null && this.webserviceInfo.hasProfile("test")) { + return true; + } + final ClientHttpRequestFactory clientHttpRequestFactory = this.clientHttpRequestFactoryService .getClientHttpRequestFactory() .getOrThrow(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java index d06abd76..fbf9d380 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java @@ -206,19 +206,6 @@ public abstract class EntityController { return page; } - protected void populateFilterMap(final FilterMap filterMap, final Long institutionId, final String sort) { - // If current user has no read access for specified entity type within other institution - // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance - if (!this.authorization.hasGrant(PrivilegeType.READ, getGrantEntityType())) { - filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); - } - - // If sorting is on institution name we need to join the institution table - if (sort != null && sort.contains(Entity.FILTER_ATTR_INSTITUTION)) { - filterMap.putIfAbsent(FilterMap.ATTR_ADD_INSITUTION_JOIN, Constants.TRUE_STRING); - } - } - // ****************** // * GET (names) // ****************** @@ -581,6 +568,19 @@ public abstract class EntityController { .getOrThrow(); } + protected void populateFilterMap(final FilterMap filterMap, final Long institutionId, final String sort) { + // If current user has no read access for specified entity type within other institution + // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance + if (!this.authorization.hasGrant(PrivilegeType.READ, getGrantEntityType())) { + filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); + } + + // If sorting is on institution name we need to join the institution table + if (sort != null && sort.contains(Entity.FILTER_ATTR_INSTITUTION)) { + filterMap.putIfAbsent(FilterMap.ATTR_ADD_INSITUTION_JOIN, Constants.TRUE_STRING); + } + } + protected EnumSet convertToEntityType(final boolean addIncludes, final List includes) { final EnumSet includeDependencies = (includes != null) ? (includes.isEmpty()) @@ -783,24 +783,6 @@ public abstract class EntityController { return this.userActivityLogDAO.logModify(entity); } - /** Makes a DELETE user activity log for the specified entity. - * This may be overwritten if the create user activity log should be skipped. - * - * @param entity the Entity instance - * @return Result refer to the logged Entity instance or to an error if happened */ - protected String logDelete(final String modelId) { - try { - return this.entityDAO - .byModelId(modelId) - .flatMap(this::logDelete) - .map(Entity::getModelId) - .getOrThrow(); - } catch (final Exception e) { - log.warn("Failed to log delete for entity id: {}", modelId, e); - return modelId; - } - } - /** Makes a DELETE user activity log for the specified entity. * This may be overwritten if the create user activity log should be skipped. * diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/GuiIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/GuiIntegrationTest.java index 5ff8dada..a002194d 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/GuiIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/GuiIntegrationTest.java @@ -41,7 +41,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.WebserviceInfoDAO; @RunWith(SpringRunner.class) @SpringBootTest( - properties = "file.encoding=UTF-8", + properties = { "file.encoding=UTF-8" }, classes = SEBServer.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @ActiveProfiles("test") @@ -62,15 +62,15 @@ public abstract class GuiIntegrationTest { @Autowired protected FilterChainProxy springSecurityFilterChain; @Autowired - private WebserviceInfoDAO webserviceInfoDAO; + protected WebserviceInfoDAO webserviceInfoDAO; @Autowired - private WebserviceInfo webserviceInfo; + protected WebserviceInfo webserviceInfo; protected MockMvc mockMvc; @Before public void setup() { - this.webserviceInfoDAO.unregister(this.webserviceInfo.getWebserviceUUID()); + //this.webserviceInfoDAO.unregister(this.webserviceInfo.getWebserviceUUID()); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac) .addFilter(this.springSecurityFilterChain).build(); diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/SEBClientBot.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/SEBClientBot.java index 05df858e..d45fb916 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/SEBClientBot.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/SEBClientBot.java @@ -96,10 +96,15 @@ public class SEBClientBot { long errorInterval = ONE_SECOND; long warnInterval = ONE_SECOND / 2; long notificationInterval = 800; - long runtime = ONE_SECOND * 3; + long runtime = ONE_SECOND * 5; int connectionAttempts = 1; - public SEBClientBot(final String clientId, final String clientSecret, final String examId, final String instId) + public SEBClientBot( + final String clientId, + final String clientSecret, + final String examId, + final String instId, + final boolean wait) throws Exception { this.clientId = clientId; @@ -114,7 +119,13 @@ public class SEBClientBot { ? this.sessionId : "connection_" + getRandomName(); - new ConnectionBot(sessionId).run(); + if (wait) { + new ConnectionBot(sessionId).run(); + } else { + new Thread(new ConnectionBot(sessionId)).start(); + } + + //new ConnectionBot(sessionId).run(); //this.executorService.execute(new ConnectionBot(sessionId)); } diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java index 9188427f..53a3ff1b 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java @@ -40,6 +40,7 @@ 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.http.HttpStatus; import org.springframework.test.context.jdbc.Sql; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -47,12 +48,15 @@ import org.springframework.util.StreamUtils; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Domain.BATCH_ACTION; import ch.ethz.seb.sebserver.gbl.model.Domain.SEB_CLIENT_CONFIGURATION; import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; @@ -62,6 +66,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport.ErrorEntry; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate; @@ -69,6 +74,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType; @@ -97,6 +103,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; +import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType; import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction; import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType; import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification; @@ -104,6 +111,7 @@ 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.session.MonitoringFullPageData; import ch.ethz.seb.sebserver.gbl.model.session.MonitoringSEBConnectionData; +import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom; 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; @@ -114,7 +122,11 @@ import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping; import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ExamConfigurationServiceImpl; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestServiceImpl; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.batch.DoBatchAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.batch.GetBatchAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.batch.GetBatchActionPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.ActivateSEBRestriction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.ArchiveExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamConsistency; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamImported; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckSEBRestriction; @@ -165,7 +177,9 @@ 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.DeleteAllClientEvents; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.DeleteAllUserLogs; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.ExportSEBClientLogs; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetUserLogNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetUserLogPage; @@ -214,18 +228,27 @@ 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.CloseProctoringRoom; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ConfirmPendingClientNotification; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.DisableClientConnection; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnection; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionDataList; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetCollectingRoomConnections; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetCollectingRooms; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnection; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnectionPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetMonitoringFullPageData; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetPendingClientNotifications; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnection; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetRunningExamPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetTownhallRoom; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.IsTownhallRoomAvailable; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.NotifyProctoringRoomOpened; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.OpenTownhallRoom; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.PropagateInstruction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.SendProctoringReconfigurationAttributes; 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.DeleteUserAccount; @@ -235,7 +258,9 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUs import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.NewUserAccount; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.RegisterNewUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.SaveUserAccount; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class UseCasesIntegrationTest extends GuiIntegrationTest { @@ -2107,6 +2132,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { new GetClientConnection(), new GetMonitoringFullPageData(), new GetExtendedClientEventPage(), + new ExportSEBClientLogs(), + new DeleteAllClientEvents(), new DisableClientConnection(), new PropagateInstruction(), new GetClientConnectionPage(), @@ -2123,7 +2150,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { new ActivateClientConfig(), new GetClientConfigPage(), new GetIndicatorPage(), - new GetIndicators()); + new GetIndicators(), + new DeleteAllClientEvents()); // get running exams final Result> runningExamsCall = restService.getBuilder(GetRunningExamPage.class) @@ -2162,7 +2190,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()).toString()); // get active client config's credentials - final Result> cconfigs = adminRestService.getBuilder(GetClientConfigPage.class) + final Result> cconfigs = adminRestService + .getBuilder(GetClientConfigPage.class) .call(); assertNotNull(cconfigs); assertFalse(cconfigs.hasError()); @@ -2171,7 +2200,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { final SEBClientConfig clientConfig = ccPage.content.get(0); assertTrue(clientConfig.isActive()); - final ClientCredentials credentials = this.sebClientConfigDAO.getSEBClientCredentials(clientConfig.getModelId()) + final ClientCredentials credentials = this.sebClientConfigDAO + .getSEBClientCredentials(clientConfig.getModelId()) .getOrThrow(); adminRestService.getBuilder(ActivateClientConfig.class) @@ -2184,8 +2214,9 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { credentials.clientIdAsString(), this.cryptor.decrypt(credentials.secret).getOrThrow().toString(), exam.getModelId(), - String.valueOf(exam.institutionId)); - Thread.sleep(1000); + String.valueOf(exam.institutionId), + true); + //Thread.sleep(1000); // send get connections connectionsCall = @@ -2263,7 +2294,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertNotNull(instructionCall); assertFalse(instructionCall.hasError()); - Thread.sleep(1000); + //Thread.sleep(1000); } catch (final Exception e) { fail(e.getMessage()); } @@ -2315,7 +2346,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertEquals("DISABLED", conData.clientConnection.status.name()); // get client logs - final Result> clientLogPage = restService.getBuilder(GetExtendedClientEventPage.class) + final Result> clientLogPage = restService + .getBuilder(GetExtendedClientEventPage.class) .call(); assertNotNull(clientLogPage); @@ -2325,6 +2357,32 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { final ExtendedClientEvent extendedClientEvent = clientLogs.content.get(0); assertNotNull(extendedClientEvent); + // export client logs + restService + .getBuilder(ExportSEBClientLogs.class) + .withQueryParam(API.SEB_CLIENT_EVENT_EXPORT_TYPE, ExportType.CSV.name()) + .withQueryParam(API.SEB_CLIENT_EVENT_EXPORT_INCLUDE_EXAMS, "true") + .withResponseExtractor(response -> { + final HttpStatus statusCode = response.getStatusCode(); + assertEquals("200 OK", statusCode.toString()); + final String csvExport = IOUtils.toString(response.getBody()); + assertTrue(StringUtils.isNotBlank(csvExport)); + return true; + }) + .call(); + + // delete client logs + final Result report = adminRestService + .getBuilder(DeleteAllClientEvents.class) + .withFormParam( + API.PARAM_MODEL_ID_LIST, + StringUtils.join( + clientLogs.content.stream().map(e -> e.getModelId()).collect(Collectors.toList()), ",")) + .call(); + + assertNotNull(report); + assertFalse(report.hasError()); + // get client connection page Result> connectionPageRes = restService .getBuilder(GetClientConnectionPage.class) @@ -2344,6 +2402,44 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { connectionPage = connectionPageRes.get(); assertNotNull(connectionPage); assertTrue(connectionPage.isEmpty()); + + Result> connectionDatacall = restService + .getBuilder(GetFinishedExamClientConnectionPage.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId()) + .call(); + assertNotNull(connectionDatacall); + assertFalse(connectionDatacall.hasError()); + Page ccDataPage = connectionDatacall.get(); + assertNotNull(ccDataPage); + assertFalse(ccDataPage.content.isEmpty()); + final ClientConnectionData clientConnectionData = ccDataPage.content.get(0); + assertNotNull(clientConnectionData); + assertEquals("DISABLED", clientConnectionData.clientConnection.status.toString()); + + connectionDatacall = restService + .getBuilder(GetFinishedExamClientConnectionPage.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId()) + .withQueryParam(ClientConnection.FILTER_ATTR_INFO, "test") + .call(); + assertNotNull(connectionDatacall); + assertFalse(connectionDatacall.hasError()); + ccDataPage = connectionDatacall.get(); + assertNotNull(ccDataPage); + assertTrue(ccDataPage.content.isEmpty()); + + final Result ccDataCall = restService + .getBuilder(GetFinishedExamClientConnection.class) + .withURIVariable(API.PARAM_MODEL_ID, clientConnectionData.getModelId()) + .call(); + + assertNotNull(ccDataCall); + assertFalse(ccDataCall.hasError()); + final ClientConnectionData clientConnectionData2 = ccDataCall.get(); + assertNotNull(clientConnectionData2); + assertEquals( + clientConnectionData2.clientConnection.connectionToken, + clientConnectionData.clientConnection.connectionToken); + } @Test @@ -3457,4 +3553,466 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .getOrThrow(); } + @Test + @Order(27) + // ************************************* + // Use Case 27: Login as admin and set exam proctoring settings for Jtisi + // - Get Exam (running) + // - Set Proctoring settings for exam + // - Check settings for exam + public void testUsecase27_SetProctoringSettingsJitsiForExam() throws IOException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetExamPage(), + new GetExamProctoringSettings(), + new SaveExamProctoringSettings(), + new IsTownhallRoomAvailable(), + new GetCollectingRooms()); + + // get exam + final Result> exams = restService + .getBuilder(GetExamPage.class) + .call(); + + assertNotNull(exams); + assertFalse(exams.hasError()); + final Page examPage = exams.get(); + assertFalse(examPage.isEmpty()); + + final Exam runningExam = examPage.content + .stream() + .filter(exam -> exam.status == ExamStatus.RUNNING) + .findFirst() + .orElse(null); + + assertNotNull(runningExam); + assertTrue(runningExam.status == ExamStatus.RUNNING); + + final Result pSettings = restService + .getBuilder(GetExamProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call(); + + assertNotNull(pSettings); + assertFalse(pSettings.hasError()); + ProctoringServiceSettings proctoringServiceSettings = pSettings.get(); + assertFalse(proctoringServiceSettings.enableProctoring); + assertNull(proctoringServiceSettings.serverURL); + + // set proctoring settings + final ProctoringServiceSettings newProctoringServiceSettings = new ProctoringServiceSettings( + runningExam.id, + true, + ProctoringServerType.JITSI_MEET, + "https://test.proc/service", + 2, + EnumSet.allOf(ProctoringFeature.class), + true, + "appKey", "appSecret", + "sdkKey", "sdkSecret", + false); + + final Result newProcSettings = restService + .getBuilder(SaveExamProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withBody(newProctoringServiceSettings) + .call(); + + assertNotNull(newProcSettings); + assertFalse(newProcSettings.hasError()); + + proctoringServiceSettings = restService + .getBuilder(GetExamProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertTrue(proctoringServiceSettings.enableProctoring); + assertEquals("https://test.proc/service", proctoringServiceSettings.serverURL); + + final String twonhallRoom = restService + .getBuilder(IsTownhallRoomAvailable.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertEquals("true", twonhallRoom); + + final Collection collectingRooms = restService + .getBuilder(GetCollectingRooms.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertNotNull(collectingRooms); + assertTrue(collectingRooms.isEmpty()); + } + + @Autowired + private ExamProctoringRoomService examProcotringRoomService; + @Autowired + private ClientConnectionDAO clientConnectionDAO; + + @Test + @Order(28) + // ************************************* + // Use Case 28: Login as admin and connect with SEBs to running exam with procotring enabled + // - Get Exam (running) + // - start some SEB clients connecting to running exam + // - Check collecting rooms created + public void testUsecase28_TestExamProctoring() throws IOException { + + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetExamPage(), + new GetExamProctoringSettings(), + new SaveExamProctoringSettings(), + new IsTownhallRoomAvailable(), + new GetCollectingRooms(), + new GetClientConfigPage(), + new ActivateClientConfig(), + new NewClientConfig(), + new GetClientConfig(), + new GetProctorRoomConnection(), + new GetCollectingRoomConnections(), + new NotifyProctoringRoomOpened(), + new SendProctoringReconfigurationAttributes(), + new GetTownhallRoom(), + new OpenTownhallRoom(), + new CloseProctoringRoom()); + + // get exam + final Result> exams = restService + .getBuilder(GetExamPage.class) + .call(); + + assertNotNull(exams); + assertFalse(exams.hasError()); + final Page examPage = exams.get(); + assertFalse(examPage.isEmpty()); + + final Exam runningExam = examPage.content + .stream() + .filter(exam -> exam.status == ExamStatus.RUNNING) + .findFirst() + .orElse(null); + + assertNotNull(runningExam); + assertTrue(runningExam.status == ExamStatus.RUNNING); + + final Result pSettings = restService + .getBuilder(GetExamProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call(); + + assertNotNull(pSettings); + assertFalse(pSettings.hasError()); + final ProctoringServiceSettings proctoringServiceSettings = pSettings.get(); + assertTrue(proctoringServiceSettings.enableProctoring); + assertEquals("https://test.proc/service", proctoringServiceSettings.serverURL); + + // start some SEB connections for this exam + + // create SEB Client Config without password protection + Result newConfigResponse = restService + .getBuilder(NewClientConfig.class) + .withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "No Password Protection") + .withFormParam(SEBClientConfig.ATTR_FALLBACK, Constants.TRUE_STRING) + .withFormParam(SEBClientConfig.ATTR_FALLBACK_START_URL, "http://fallback.com/fallback") + .withFormParam(SEBClientConfig.ATTR_FALLBACK_TIMEOUT, "100") + .withFormParam(SEBClientConfig.ATTR_FALLBACK_ATTEMPTS, "5") + .withFormParam(SEBClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL, "5") + .withFormParam(SEBClientConfig.ATTR_CONFIG_PURPOSE, SEBClientConfig.ConfigPurpose.START_EXAM.name()) + .call(); + + assertNotNull(newConfigResponse); + assertFalse(newConfigResponse.hasError()); + final SEBClientConfig sebClientConfig = newConfigResponse.get(); + assertEquals("No Password Protection", sebClientConfig.name); + assertFalse(sebClientConfig.isActive()); + assertEquals("http://fallback.com/fallback", sebClientConfig.fallbackStartURL); + + // activate the new Client Configuration + restService + .getBuilder(ActivateClientConfig.class) + .withURIVariable(API.PARAM_MODEL_ID, sebClientConfig.getModelId()) + .call(); + + newConfigResponse = restService.getBuilder(GetClientConfig.class) + .withURIVariable(API.PARAM_MODEL_ID, sebClientConfig.getModelId()) + .call(); + + final SEBClientConfig clientConfig = newConfigResponse.get(); + assertTrue(clientConfig.isActive()); + final ClientCredentials credentials = this.sebClientConfigDAO + .getSEBClientCredentials(clientConfig.getModelId()) + .getOrThrow(); + + assertTrue(clientConfig.isActive()); + + // simulate a SEB connection + try { + new SEBClientBot( + credentials.clientIdAsString(), + this.cryptor.decrypt(credentials.secret).getOrThrow().toString(), + runningExam.getModelId(), + String.valueOf(runningExam.institutionId), + false); + + Thread.sleep(1000); + + this.examProcotringRoomService.updateProctoringCollectingRooms(); + + // check collecting room was created + final Collection collectingRooms = restService + .getBuilder(GetCollectingRooms.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertNotNull(collectingRooms); + assertFalse(collectingRooms.isEmpty()); + // Two rooms a two people for four connections + assertEquals(2, collectingRooms.size()); + final RemoteProctoringRoom room1 = collectingRooms.iterator().next(); + assertEquals(2, room1.roomSize.intValue()); + assertFalse(room1.townhallRoom); + + final ProctoringRoomConnection proctoringRoomConnection = restService + .getBuilder(GetProctorRoomConnection.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, room1.name) + .call() + .get(); + + assertNotNull(proctoringRoomConnection); + assertEquals(room1.name, proctoringRoomConnection.roomName); + assertNotNull(proctoringRoomConnection.accessToken); + + // notify room open + restService + .getBuilder(NotifyProctoringRoomOpened.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, room1.name) + .call() + .get(); + + // reconfigure clients in room + restService + .getBuilder(SendProctoringReconfigurationAttributes.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, room1.name) + .withQueryParam(API.EXAM_PROCTORING_ATTR_RECEIVE_AUDIO, "true") + .withQueryParam(API.EXAM_PROCTORING_ATTR_RECEIVE_VIDEO, "true") + .withQueryParam(API.EXAM_PROCTORING_ATTR_ALLOW_CHAT, "true") + .call() + .get(); + + final Collection collection = restService + .getBuilder(GetCollectingRoomConnections.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, room1.name) + .call() + .get(); + + assertNotNull(collection); + assertFalse(collection.isEmpty()); + assertEquals(2, collection.size()); + final ClientConnection connection = collection.iterator().next(); + assertEquals(runningExam.id, connection.examId); + // this is because the Json model do not contian certain attributes due to performance + assertNull(connection.remoteProctoringRoomId); + // we can geht the room number by getting it directyl from the record + final ClientConnection clientConnection = this.clientConnectionDAO.byPK(connection.id).get(); + assertNotNull(clientConnection.remoteProctoringRoomId); + + // get and open townhall + final String townhallActive = restService + .getBuilder(IsTownhallRoomAvailable.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertEquals("true", townhallActive); + + // check no Townhallroom yet + RemoteProctoringRoom townhallRoom = restService + .getBuilder(GetTownhallRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + assertEquals(RemoteProctoringRoom.NULL_ROOM, townhallRoom); + + // open townhall room + final ProctoringRoomConnection townhallRoomConntection = restService + .getBuilder(OpenTownhallRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertNotNull(townhallRoomConntection); + + // check Townhallroom is available yet + townhallRoom = restService + .getBuilder(GetTownhallRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + assertTrue(townhallRoom.townhallRoom); + assertEquals(townhallRoom.name, townhallRoomConntection.roomName); + + // close townhall room + restService + .getBuilder(CloseProctoringRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, townhallRoom.name) + .call() + .get(); + + townhallRoom = restService + .getBuilder(GetTownhallRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + assertEquals(RemoteProctoringRoom.NULL_ROOM, townhallRoom); + + Thread.sleep(5000); + + } catch (final Exception e) { + fail(e.getMessage()); + } + } + + @Test + @Order(29) + // ************************************* + // Use Case 29: Login as admin and create some batch actions + // - Get Exam (running) + // - start some SEB clients connecting to running exam + // - Check collecting rooms created + public void testUsecase29_TestBatchAction() throws IOException, InterruptedException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new DoBatchAction(), + new GetBatchAction(), + new GetBatchActionPage(), + new GetExamConfigNodePage()); + + final ConfigurationNode config = restService + .getBuilder(GetExamConfigNodePage.class) + .call() + .getOrThrow().content + .get(0); + assertNotNull(config); + assertEquals("READY_TO_USE", config.status.toString()); + + // apply batch action + final Result doBatchAction = restService + .getBuilder(DoBatchAction.class) + .withFormParam(Domain.BATCH_ACTION.ATTR_ACTION_TYPE, BatchActionType.EXAM_CONFIG_STATE_CHANGE.name()) + .withFormParam(BATCH_ACTION.ATTR_SOURCE_IDS, config.getModelId()) + .withFormParam(BatchAction.ACTION_ATTRIBUT_TARGET_STATE, ConfigurationStatus.CONSTRUCTION.name()) + .call(); + + assertNotNull(doBatchAction); + assertFalse(doBatchAction.hasError()); + final BatchAction batchAction = doBatchAction.get(); + assertNotNull(batchAction); + assertNotNull(batchAction.ownerId); + assertFalse(batchAction.isFinished()); + assertEquals("EXAM_CONFIG_STATE_CHANGE", batchAction.actionType.name()); + + Thread.sleep(1000); + + final BatchAction savedBatchAction = restService + .getBuilder(GetBatchAction.class) + .withURIVariable(API.PARAM_MODEL_ID, batchAction.getModelId()) + .call().get(); + + assertNotNull(savedBatchAction); + assertNotNull(savedBatchAction.ownerId); + assertTrue(savedBatchAction.isFinished()); + assertEquals("EXAM_CONFIG_STATE_CHANGE", savedBatchAction.actionType.name()); + assertNotNull(savedBatchAction.processorId); + + final Page page = restService + .getBuilder(GetBatchActionPage.class) + .call().get(); + + assertNotNull(page); + assertFalse(page.content.isEmpty()); + } + + @Test + @Order(30) + // ************************************* + // Use Case 30: Login as admin and archive finished exam + // - Get Exam (finished), archive and check + public void testUsecase30_TestArchiveExam() throws IOException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetExamPage(), + new GetExam(), + new ArchiveExam()); + + final Page finishedExams = restService + .getBuilder(GetExamPage.class) + .withQueryParam(Exam.FILTER_ATTR_STATUS, ExamStatus.FINISHED.name()) + .call() + .get(); + + assertNotNull(finishedExams); + assertFalse(finishedExams.content.isEmpty()); + final Exam exam = finishedExams.content.get(0); + assertEquals(ExamStatus.FINISHED, exam.status); + + final Result archiveCall = restService.getBuilder(ArchiveExam.class) + .withURIVariable(API.PARAM_MODEL_ID, exam.getModelId()) + .call(); + + assertNotNull(archiveCall); + assertFalse(archiveCall.hasError()); + final Exam exam2 = archiveCall.get(); + assertNotNull(exam2); + assertEquals(exam.id, exam2.id); + assertEquals(ExamStatus.ARCHIVED, exam2.status); + } + + @Test + @Order(31) + // ************************************* + // Use Case 31: Login as admin and archive finished exam + // - Get Exam (running), archive and check not possible + public void testUsecase31_TestArchiveRunningExam_NotPossible() throws IOException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetExamPage(), + new GetExam(), + new ArchiveExam()); + + final Page finishedExams = restService + .getBuilder(GetExamPage.class) + .withQueryParam(Exam.FILTER_ATTR_STATUS, ExamStatus.RUNNING.name()) + .call() + .get(); + + assertNotNull(finishedExams); + assertFalse(finishedExams.content.isEmpty()); + final Exam exam = finishedExams.content.get(0); + assertEquals(ExamStatus.RUNNING, exam.status); + + final Result archiveCall = restService.getBuilder(ArchiveExam.class) + .withURIVariable(API.PARAM_MODEL_ID, exam.getModelId()) + .call(); + + assertNotNull(archiveCall); + assertTrue(archiveCall.hasError()); + assertTrue(archiveCall.getError().getMessage().contains("Exam is in wrong status to archive")); + } + } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ConfigurationAttributeAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ConfigurationAttributeAPITest.java new file mode 100644 index 00000000..af5c0ad4 --- /dev/null +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ConfigurationAttributeAPITest.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2022 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.webservice.integration.api.admin; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityName; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.UserServiceImpl; +import ch.ethz.seb.sebserver.webservice.weblayer.api.ConfigurationAttributeController; + +@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" }) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class ConfigurationAttributeAPITest extends AdministrationAPIIntegrationTester { + + @Autowired + private ConfigurationAttributeController configurationAttributeController; + @Autowired + private UserServiceImpl userServiceImpl; + @Mock + private HttpServletRequest mockRequest; + + private final MultiValueMap params = new LinkedMultiValueMap<>(); + + @Before + public void init() { + this.userServiceImpl.setAuthenticationIfAbsent(new SEBServerUser( + -1L, + new UserInfo("user1", 1L, null, "admin", null, null, null, true, null, null, + EnumSet.allOf(UserRole.class).stream().map(r -> r.name()).collect(Collectors.toSet())), + null)); + Mockito.when(this.mockRequest.getQueryString()).thenReturn(""); + } + + @Test + @Order(1) + public void test1_GetPage() { + final Page page = this.configurationAttributeController.getPage( + 1L, 0, 100, null, + new LinkedMultiValueMap(), + this.mockRequest); + + assertNotNull(page); + assertFalse(page.content.isEmpty()); + assertEquals("100", String.valueOf(page.content.size())); + } + + @Test + @Order(2) + public void test2_GetNames() { + Collection names = this.configurationAttributeController.getNames( + 1L, + new LinkedMultiValueMap(), + this.mockRequest); + + assertNotNull(names); + assertFalse(names.isEmpty()); + assertEquals("241", String.valueOf(names.size())); + + this.params.clear(); + this.params.add(ConfigurationAttribute.FILTER_ATTR_TYPE, AttributeType.CHECKBOX.name()); + + names = this.configurationAttributeController.getNames( + 1L, + this.params, + this.mockRequest); + + assertNotNull(names); + assertFalse(names.isEmpty()); + assertEquals("139", String.valueOf(names.size())); + } + + @Test + @Order(3) + public void test3_GetSingle() { + final ConfigurationAttribute attr = this.configurationAttributeController.getBy("1"); + + assertNotNull(attr); + assertEquals("hashedAdminPassword", attr.name); + } + + @Test + @Order(4) + public void test4_GetList() { + List forIds = this.configurationAttributeController.getForIds("1,2"); + + assertNotNull(forIds); + assertEquals("2", String.valueOf(forIds.size())); + + forIds = this.configurationAttributeController.getForIds(null); + + assertNotNull(forIds); + assertEquals("241", String.valueOf(forIds.size())); + } + + @Test + @Order(5) + public void test5_CreateAndSaveAndDelete() { + this.params.clear(); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_PARENT_ID, null); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME, "testAttribute"); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE, AttributeType.CHECKBOX.name()); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_RESOURCES, ""); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_VALIDATOR, ""); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_DEPENDENCIES, ""); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_DEFAULT_VALUE, "true"); + + final ConfigurationAttribute create = this.configurationAttributeController.create( + this.params, + 1L, + this.mockRequest); + + assertNotNull(create); + assertNotNull(create.id); + assertEquals("testAttribute", create.name); + assertEquals("true", create.defaultValue); + + final ConfigurationAttribute savePut = this.configurationAttributeController.savePut(new ConfigurationAttribute( + create.id, + null, null, null, null, null, null, + "false")); + + assertNotNull(savePut); + assertNotNull(savePut.id); + assertEquals("testAttribute", savePut.name); + assertEquals("false", savePut.defaultValue); + + } + + @Test + @Order(6) + public void test6_NoDeletionSupport() { + + try { + this.configurationAttributeController.hardDeleteAll( + Arrays.asList("1,2,3"), + false, + null, + 1L); + fail("Error expected here"); + } catch (final Exception e) { + assertEquals( + "No bulk action support for: BulkAction [type=HARD_DELETE, sourceType=CONFIGURATION_ATTRIBUTE, sources=[]]", + e.getMessage()); + } + + try { + this.configurationAttributeController.hardDelete( + "1", + false, + null); + fail("Error expected here"); + } catch (final Exception e) { + assertEquals( + "No bulk action support for: BulkAction [type=HARD_DELETE, sourceType=CONFIGURATION_ATTRIBUTE, sources=[EntityName [entityType=CONFIGURATION_ATTRIBUTE, modelId=1, name=hashedAdminPassword]]]", + e.getMessage()); + } + + } + +} diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamJITSIProctoringServiceTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamJITSIProctoringServiceTest.java index d5741993..6e1db631 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamJITSIProctoringServiceTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamJITSIProctoringServiceTest.java @@ -81,7 +81,7 @@ public class ExamJITSIProctoringServiceTest { final Cryptor cryptorMock = Mockito.mock(Cryptor.class); Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn(Result.of("fbvgeghergrgrthrehreg123")); final JitsiProctoringService examJITSIProctoringService = - new JitsiProctoringService(null, null, cryptorMock, null, new JSONMapper()); + new JitsiProctoringService(null, null, cryptorMock, null, new JSONMapper(), null); String accessToken = examJITSIProctoringService.createPayload( "test-app", @@ -115,7 +115,7 @@ public class ExamJITSIProctoringServiceTest { final Cryptor cryptorMock = Mockito.mock(Cryptor.class); Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn(Result.of("fbvgeghergrgrthrehreg123")); final JitsiProctoringService examJITSIProctoringService = - new JitsiProctoringService(null, null, cryptorMock, null, new JSONMapper()); + new JitsiProctoringService(null, null, cryptorMock, null, new JSONMapper(), null); final ProctoringRoomConnection data = examJITSIProctoringService.createProctoringConnection( "connectionToken", "https://seb-jitsi.example.ch", @@ -160,7 +160,7 @@ public class ExamJITSIProctoringServiceTest { examSessionService, cryptor, clientHttpRequestFactoryService, - jsonMapper); + jsonMapper, null); } } diff --git a/src/test/resources/schema-test.sql b/src/test/resources/schema-test.sql index dfc6b80f..93d5a6ef 100644 --- a/src/test/resources/schema-test.sql +++ b/src/test/resources/schema-test.sql @@ -556,7 +556,6 @@ CREATE TABLE IF NOT EXISTS `seb_client_configuration` ( -- ----------------------------------------------------- -- Table `webservice_server_info` -- ----------------------------------------------------- -DROP TABLE IF EXISTS `webservice_server_info` ; CREATE TABLE IF NOT EXISTS `webservice_server_info` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,