You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

840 lines
41 KiB
Java

package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.SchoolClass;
import com.sztzjy.linkCommerce.entity.TaskAllocation;
import com.sztzjy.linkCommerce.entity.TeachingClassStudent;
import com.sztzjy.linkCommerce.entity.TrainingTask;
import com.sztzjy.linkCommerce.entity.TrainingTaskClassConfig;
import com.sztzjy.linkCommerce.entity.importDto.TrainingTaskImportDTO;
import com.sztzjy.linkCommerce.mapper.SchoolClassMapper;
import com.sztzjy.linkCommerce.mapper.TaskAllocationMapper;
import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper;
import com.sztzjy.linkCommerce.mapper.TrainingTaskClassConfigMapper;
import com.sztzjy.linkCommerce.mapper.TrainingTaskMapper;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
class TrainingTaskServiceImplTest {
@Test
void splitListSupportsCommonSeparators() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
List<String> result = service.splitList("one;two|three");
assertEquals(3, result.size());
assertEquals("one", result.get(0));
assertEquals("three", result.get(2));
}
@Test
void importTaskValidatesRequiredFields() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
TrainingTaskImportDTO row = new TrainingTaskImportDTO();
row.setTaskName("Teacher Title");
List<String> errors = new ArrayList<>();
TrainingTask task = service.buildTask(row, 2, errors);
assertEquals(null, task);
assertFalse(errors.isEmpty());
}
@Test
void importTaskAllowsTeacherEditedTaskNameForBuiltInTask() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
TrainingTaskImportDTO row = new TrainingTaskImportDTO();
row.setProjectName("Foundation");
row.setTaskKey("new-product-survey");
row.setTaskName("Teacher Edited Survey");
row.setSteps("s1;s2");
List<String> errors = new ArrayList<>();
TrainingTask task = service.buildTask(row, 2, errors);
assertEquals(0, errors.size());
assertEquals("Teacher Edited Survey", task.getTaskName());
assertTrue(task.getSteps().contains("\"name\":\"s1\""));
assertTrue(task.getSteps().contains("\"name\":\"s2\""));
assertFalse(task.getSteps().contains("Step 3"));
}
@Test
void importTaskRejectsUnknownTaskKeyBecauseTaskCountIsFixed() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
TrainingTaskImportDTO row = new TrainingTaskImportDTO();
row.setProjectName("Custom");
row.setTaskKey("custom-task");
row.setTaskName("Custom Task");
List<String> errors = new ArrayList<>();
TrainingTask task = service.buildTask(row, 2, errors);
assertEquals(null, task);
assertFalse(errors.isEmpty());
}
@Test
void updateTaskPersistsTeacherEditedTaskNameAndActualStepCount() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
TrainingTask stored = storedTask("task-1", "new-product-survey");
when(service.trainingTaskMapper.selectByPrimaryKey("task-1")).thenReturn(stored);
TrainingTask payload = new TrainingTask();
payload.setProjectName("Changed Project Should Be Ignored");
payload.setTaskKey("unknown-key-should-be-ignored");
payload.setTaskName("Teacher Edited Survey");
payload.setSteps("[\"Only One\"]");
service.update("task-1", payload);
ArgumentCaptor<TrainingTask> captor = ArgumentCaptor.forClass(TrainingTask.class);
verify(service.trainingTaskMapper).updateByPrimaryKeySelective(captor.capture());
assertEquals("new-product-survey", captor.getValue().getTaskKey());
assertEquals("Foundation", captor.getValue().getProjectName());
assertEquals("Teacher Edited Survey", captor.getValue().getTaskName());
assertTrue(captor.getValue().getSteps().contains("\"name\":\"Only One\""));
assertFalse(captor.getValue().getSteps().contains("Step 2"));
}
@Test
void updateTaskPreservesStepsBeyondTheBuiltInCount() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
TrainingTask stored = storedTask("task-1", "new-product-survey");
when(service.trainingTaskMapper.selectByPrimaryKey("task-1")).thenReturn(stored);
TrainingTask payload = new TrainingTask();
payload.setProjectName("Foundation");
payload.setTaskKey("new-product-survey");
payload.setTaskName("Teacher Edited Survey");
payload.setSteps("[\"Step 1\",\"Step 2\",\"Step 3\",\"Step 4\",\"Custom Step\"]");
service.update("task-1", payload);
ArgumentCaptor<TrainingTask> captor = ArgumentCaptor.forClass(TrainingTask.class);
verify(service.trainingTaskMapper).updateByPrimaryKeySelective(captor.capture());
assertTrue(captor.getValue().getSteps().contains("Custom Step"));
}
@Test
void createRejectsUnknownTaskKeyBecauseTaskCountIsFixed() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
TrainingTask task = new TrainingTask();
task.setProjectName("Custom");
task.setTaskKey("custom-task");
task.setTaskName("Custom Task");
assertThrows(IllegalArgumentException.class, () -> service.create(task, "teacher-1"));
}
@Test
void deleteRejectsBuiltInTaskBecauseTaskCountIsFixed() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
assertThrows(IllegalArgumentException.class, () -> service.delete("task-1"));
}
@Test
void importTaskUpdatesExistingTaskByTaskKey() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
TrainingTask existing = storedTask("task-1", "new-product-survey");
when(service.trainingTaskMapper.selectByTaskKey("new-product-survey")).thenReturn(existing);
when(service.trainingTaskMapper.selectByPrimaryKey("task-1")).thenReturn(existing);
TrainingTaskImportDTO row = new TrainingTaskImportDTO();
row.setProjectName("Foundation");
row.setTaskKey("new-product-survey");
row.setTaskName("Teacher Edited Survey");
row.setObjectives("goal1;goal2");
row.setSteps("s1;s2;s3;s4;s5");
row.setEnabled("true");
List<String> errors = new ArrayList<>();
int count = service.importTasks(Collections.singletonList(row), "teacher-1", errors);
assertEquals(1, count);
assertEquals(0, errors.size());
ArgumentCaptor<TrainingTask> captor = ArgumentCaptor.forClass(TrainingTask.class);
verify(service.trainingTaskMapper).updateByPrimaryKeySelective(captor.capture());
assertEquals("task-1", captor.getValue().getId());
assertEquals("Teacher Edited Survey", captor.getValue().getTaskName());
assertTrue(captor.getValue().getSteps().contains("\"name\":\"s5\""));
assertTrue(captor.getValue().getSteps().contains("\"id\":\"custom-5\""));
}
@Test
void importTaskRejectsMissingBuiltInRecordRatherThanCreatingNewTask() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
when(service.trainingTaskMapper.selectByTaskKey("new-product-survey")).thenReturn(null);
TrainingTaskImportDTO row = new TrainingTaskImportDTO();
row.setProjectName("Foundation");
row.setTaskKey("new-product-survey");
row.setTaskName("Teacher Edited Survey");
row.setObjectives("goal1;goal2");
row.setSteps("s1;s2;s3;s4");
row.setEnabled("true");
List<String> errors = new ArrayList<>();
int count = service.importTasks(Collections.singletonList(row), "teacher-1", errors);
assertEquals(0, count);
assertFalse(errors.isEmpty());
verify(service.trainingTaskMapper, never()).insertSelective(any());
}
@Test
void ensureDefaultTasksBackfillsMissingBuiltInTasksWhenTableAlreadyHasData() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
TrainingTask existing = storedTask("task-1", "new-product-survey");
when(service.trainingTaskMapper.selectByTaskKey("new-product-survey")).thenReturn(existing);
service.ensureDefaultTasks("system");
ArgumentCaptor<TrainingTask> captor = ArgumentCaptor.forClass(TrainingTask.class);
verify(service.trainingTaskMapper, times(23)).insertSelective(captor.capture());
assertFalse(captor.getAllValues().stream().anyMatch(task -> "new-product-survey".equals(task.getTaskKey())));
TrainingTask comprehensive = captor.getAllValues().get(captor.getAllValues().size() - 1);
assertEquals("comprehensive-case-training", comprehensive.getTaskKey());
assertEquals("综合实训", comprehensive.getProjectName());
assertEquals("综合案例实训", comprehensive.getTaskName());
}
@Test
void ensureDefaultTasksCreatesTrainingTaskTableBeforeQuerying() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.jdbcTemplate = mock(JdbcTemplate.class);
service.ensureDefaultTasks("system");
verify(service.jdbcTemplate).execute(startsWith("CREATE TABLE IF NOT EXISTS training_task ("));
verify(service.jdbcTemplate).execute("ALTER TABLE training_task CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
}
@Test
void ensureDefaultTasksCreatesClassConfigTableBeforeClassScopedQueries() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.jdbcTemplate = mock(JdbcTemplate.class);
service.ensureDefaultTasks("system");
verify(service.jdbcTemplate).execute(startsWith("CREATE TABLE IF NOT EXISTS training_task_class_config"));
}
@Test
void listDoesNotFilterDisabledTasksWhenEnabledOnlyIsFalse() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
when(service.trainingTaskMapper.selectByTaskKey(any())).thenReturn(new TrainingTask());
TrainingTask disabledTask = new TrainingTask();
disabledTask.setEnabled(Boolean.FALSE);
when(service.trainingTaskMapper.selectList(isNull(), isNull(), isNull()))
.thenReturn(Collections.singletonList(disabledTask));
List<TrainingTask> result = service.list(null, null, false);
assertEquals(1, result.size());
assertFalse(result.get(0).getEnabled());
verify(service.trainingTaskMapper, times(2)).selectList(isNull(), isNull(), isNull());
}
@Test
void studentTaskUsesClassOverrideBeforeDefault() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.teachingClassStudentMapper = mock(TeachingClassStudentMapper.class);
TeachingClassStudent membership = new TeachingClassStudent();
membership.setTeachingClassId("class-1");
when(service.teachingClassStudentMapper.selectActiveByStudentUserId("stu-1")).thenReturn(membership);
TrainingTask defaultTask = storedTask("task-1", "new-product-survey");
defaultTask.setTaskName("Default Name");
defaultTask.setKnowledge("Default Knowledge");
when(service.trainingTaskMapper.selectByTaskKey("new-product-survey")).thenReturn(defaultTask);
TrainingTaskClassConfig override = overrideTask("config-1", "class-1", "new-product-survey");
override.setTaskName("Class Name");
override.setKnowledge("Class Knowledge");
override.setEnabled(Boolean.TRUE);
when(service.trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey("class-1", "new-product-survey")).thenReturn(override);
TrainingTask result = service.getStudentTaskByTaskKey("new-product-survey", "stu-1");
assertEquals("Class Name", result.getTaskName());
assertEquals("Class Knowledge", result.getKnowledge());
}
@Test
void studentTaskFallsBackToDefaultWhenClassHasNoOverride() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.teachingClassStudentMapper = mock(TeachingClassStudentMapper.class);
TeachingClassStudent membership = new TeachingClassStudent();
membership.setTeachingClassId("class-1");
when(service.teachingClassStudentMapper.selectActiveByStudentUserId("stu-1")).thenReturn(membership);
TrainingTask defaultTask = storedTask("task-1", "new-product-survey");
defaultTask.setTaskName("Default Name");
when(service.trainingTaskMapper.selectByTaskKey("new-product-survey")).thenReturn(defaultTask);
TrainingTask result = service.getStudentTaskByTaskKey("new-product-survey", "stu-1");
assertEquals("Default Name", result.getTaskName());
}
@Test
void teacherCannotEditClassCreatedByAnotherTeacher() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = new SchoolClass();
schoolClass.setSchoolClassId("class-2");
schoolClass.setClassType("TEACHING");
schoolClass.setCreatedBy("teacher-a");
when(service.schoolClassMapper.selectByPrimaryKey("class-2")).thenReturn(schoolClass);
TrainingTask task = storedTask(null, "new-product-survey");
assertThrows(IllegalArgumentException.class, () -> service.saveClassTask("class-2", "new-product-survey", task, "teacher-b"));
}
@Test
void teacherCanReadAnotherTeachersTeachingClassInTheSameSchool() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = teachingClass("class-2", "teacher-a");
schoolClass.setSchoolId("school-1");
when(service.schoolClassMapper.selectByPrimaryKey("class-2")).thenReturn(schoolClass);
JwtUser reader = new JwtUser();
reader.setUserId("teacher-b");
reader.setSchoolId("school-1");
assertEquals(schoolClass, service.requireReadableTeachingClass("class-2", reader));
}
@Test
void teacherCannotReadTeachingClassFromAnotherSchool() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = teachingClass("class-2", "teacher-a");
schoolClass.setSchoolId("school-1");
when(service.schoolClassMapper.selectByPrimaryKey("class-2")).thenReturn(schoolClass);
JwtUser reader = new JwtUser();
reader.setUserId("teacher-b");
reader.setSchoolId("school-2");
assertThrows(IllegalArgumentException.class,
() -> service.requireReadableTeachingClass("class-2", reader));
}
@Test
void nonOwnerSameSchoolClassTaskReadDoesNotWriteConfigurations() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = teachingClass("class-2", "teacher-a");
schoolClass.setSchoolId("school-1");
when(service.schoolClassMapper.selectByPrimaryKey("class-2")).thenReturn(schoolClass);
TrainingTask defaultTask = storedTask("task-1", "new-product-survey");
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(Collections.singletonList(defaultTask));
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-2", null)).thenReturn(Collections.emptyList());
JwtUser reader = new JwtUser();
reader.setUserId("teacher-b");
reader.setSchoolId("school-1");
List<TrainingTask> result = service.listForTeachingClass("class-2", false, reader);
assertEquals(1, result.size());
verify(service.trainingTaskClassConfigMapper, never()).insertSelective(any());
verify(service.trainingTaskClassConfigMapper, never()).updateByPrimaryKeySelective(any());
verify(service.trainingTaskClassConfigMapper, never()).deleteByTeachingClass(anyString());
verify(service.trainingTaskClassConfigMapper, never()).deleteByTeachingClassAndTaskKey(anyString(), anyString());
}
@Test
void ownerClassTaskReadDoesNotWriteConfigurations() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = teachingClass("class-2", "teacher-a");
schoolClass.setSchoolId("school-1");
when(service.schoolClassMapper.selectByPrimaryKey("class-2")).thenReturn(schoolClass);
TrainingTask defaultTask = storedTask("task-1", "new-product-survey");
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(Collections.singletonList(defaultTask));
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-2", null)).thenReturn(Collections.emptyList());
JwtUser owner = new JwtUser();
owner.setUserId("teacher-a");
owner.setSchoolId("school-1");
List<TrainingTask> result = service.listForTeachingClass("class-2", false, owner);
assertEquals(1, result.size());
verify(service.trainingTaskClassConfigMapper, never()).insertSelective(any());
verify(service.trainingTaskClassConfigMapper, never()).updateByPrimaryKeySelective(any());
verify(service.trainingTaskClassConfigMapper, never()).deleteByTeachingClass(anyString());
verify(service.trainingTaskClassConfigMapper, never()).deleteByTeachingClassAndTaskKey(anyString(), anyString());
}
@Test
void teacherCannotReadTeachingClassWhenSchoolIdIsMissing() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = teachingClass("class-2", "teacher-a");
when(service.schoolClassMapper.selectByPrimaryKey("class-2")).thenReturn(schoolClass);
JwtUser reader = new JwtUser();
reader.setUserId("teacher-b");
assertThrows(IllegalArgumentException.class,
() -> service.requireReadableTeachingClass("class-2", reader));
}
@Test
void teacherCannotReadTeachingClassWhenClassSchoolIdIsMissing() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = teachingClass("class-2", "teacher-a");
when(service.schoolClassMapper.selectByPrimaryKey("class-2")).thenReturn(schoolClass);
JwtUser reader = new JwtUser();
reader.setUserId("teacher-b");
reader.setSchoolId("school-1");
assertThrows(IllegalArgumentException.class,
() -> service.requireReadableTeachingClass("class-2", reader));
}
@Test
void restoreClassTaskDefaultDeletesOnlySelectedClassTaskCopy() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
when(service.schoolClassMapper.selectByPrimaryKey("class-1")).thenReturn(teachingClass("class-1", "teacher-1"));
service.restoreClassTaskDefault("class-1", "new-product-survey", "teacher-1");
verify(service.trainingTaskClassConfigMapper)
.deleteByTeachingClassAndTaskKey("class-1", "new-product-survey");
verify(service.trainingTaskClassConfigMapper, never()).deleteByTeachingClass(anyString());
}
@Test
void restoreClassTasksDefaultsDeletesOnlySelectedClassCopies() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
when(service.schoolClassMapper.selectByPrimaryKey("class-1")).thenReturn(teachingClass("class-1", "teacher-1"));
service.restoreClassTasksDefaults("class-1", "teacher-1");
verify(service.trainingTaskClassConfigMapper).deleteByTeachingClass("class-1");
}
@Test
void teacherCannotRestoreAnotherTeachersClass() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
when(service.schoolClassMapper.selectByPrimaryKey("class-2")).thenReturn(teachingClass("class-2", "teacher-a"));
assertThrows(IllegalArgumentException.class,
() -> service.restoreClassTasksDefaults("class-2", "teacher-b"));
verifyNoInteractions(service.trainingTaskClassConfigMapper);
}
@Test
void saveClassTaskInsertsNewOverrideWhenNoneExists() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = new SchoolClass();
schoolClass.setSchoolClassId("class-1");
schoolClass.setClassType("TEACHING");
schoolClass.setCreatedBy("teacher-1");
when(service.schoolClassMapper.selectByPrimaryKey("class-1")).thenReturn(schoolClass);
TrainingTask defaultTask = storedTask("task-1", "new-product-survey");
defaultTask.setProjectName("Default Project");
defaultTask.setSort(3);
defaultTask.setKnowledge("Default Knowledge");
when(service.trainingTaskMapper.selectByTaskKey("new-product-survey")).thenReturn(defaultTask);
TrainingTask payload = storedTask(null, "new-product-survey");
payload.setProjectName("Changed Project");
payload.setTaskName("Class Name");
payload.setKnowledge("Class Knowledge");
payload.setMaterialName("Class Material");
payload.setEnabled(Boolean.TRUE);
service.saveClassTask("class-1", "new-product-survey", payload, "teacher-1");
ArgumentCaptor<TrainingTaskClassConfig> captor = ArgumentCaptor.forClass(TrainingTaskClassConfig.class);
verify(service.trainingTaskClassConfigMapper, times(25)).insertSelective(captor.capture());
TrainingTaskClassConfig savedOverride = captor.getAllValues().stream()
.filter(config -> "new-product-survey".equals(config.getTaskKey()))
.filter(config -> "Class Name".equals(config.getTaskName()))
.findFirst()
.orElseThrow(AssertionError::new);
assertEquals("class-1", savedOverride.getTeachingClassId());
assertEquals("Default Project", savedOverride.getProjectName());
assertEquals(Integer.valueOf(3), savedOverride.getSort());
assertEquals("Class Knowledge", savedOverride.getKnowledge());
assertEquals("Class Material", savedOverride.getMaterialName());
}
@Test
void disabledClassOverrideMasksEnabledDefaultInStudentDetail() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.teachingClassStudentMapper = mock(TeachingClassStudentMapper.class);
TeachingClassStudent membership = new TeachingClassStudent();
membership.setTeachingClassId("class-1");
when(service.teachingClassStudentMapper.selectActiveByStudentUserId("stu-1")).thenReturn(membership);
TrainingTask defaultTask = storedTask("task-1", "new-product-survey");
defaultTask.setEnabled(Boolean.TRUE);
when(service.trainingTaskMapper.selectByTaskKey("new-product-survey")).thenReturn(defaultTask);
TrainingTaskClassConfig override = overrideTask("config-1", "class-1", "new-product-survey");
override.setEnabled(Boolean.FALSE);
when(service.trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey("class-1", "new-product-survey")).thenReturn(override);
TrainingTask result = service.getStudentTaskByTaskKey("new-product-survey", "stu-1");
assertNull(result);
}
@Test
void disabledClassOverrideMasksEnabledDefaultInStudentList() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.teachingClassStudentMapper = mock(TeachingClassStudentMapper.class);
TeachingClassStudent membership = new TeachingClassStudent();
membership.setTeachingClassId("class-1");
when(service.teachingClassStudentMapper.selectActiveByStudentUserId("stu-1")).thenReturn(membership);
TrainingTask defaultTask = storedTask("task-1", "new-product-survey");
defaultTask.setEnabled(Boolean.TRUE);
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(Collections.singletonList(defaultTask));
TrainingTaskClassConfig override = overrideTask("config-1", "class-1", "new-product-survey");
override.setEnabled(Boolean.FALSE);
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-1", null)).thenReturn(Collections.singletonList(override));
List<TrainingTask> result = service.listForStudent("stu-1", true);
assertEquals(0, result.size());
}
@Test
void disabledClassOverrideMasksEnabledDefaultInTeacherClassList() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
SchoolClass schoolClass = new SchoolClass();
schoolClass.setSchoolClassId("class-1");
schoolClass.setClassType("TEACHING");
schoolClass.setCreatedBy("teacher-1");
schoolClass.setSchoolId("school-1");
when(service.schoolClassMapper.selectByPrimaryKey("class-1")).thenReturn(schoolClass);
TrainingTask defaultTask = storedTask("task-1", "new-product-survey");
defaultTask.setEnabled(Boolean.TRUE);
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(Collections.singletonList(defaultTask));
TrainingTaskClassConfig override = overrideTask("config-1", "class-1", "new-product-survey");
override.setEnabled(Boolean.FALSE);
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-1", null)).thenReturn(Collections.singletonList(override));
JwtUser teacher = new JwtUser();
teacher.setUserId("teacher-1");
teacher.setSchoolId("school-1");
List<TrainingTask> result = service.listForTeachingClass("class-1", true, teacher);
assertEquals(0, result.size());
}
@Test
void studentListHidesComprehensiveTrainingUntilTheClassHasPublishedIt() {
TrainingTaskServiceImpl service = studentTaskService("class-1");
TrainingTask legacy = storedTask("task-1", "new-product-survey");
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(List.of(legacy, comprehensive));
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-1", null))
.thenReturn(List.of(overrideTask("config-1", "class-1", "new-product-survey"),
overrideTask("config-2", "class-1", "comprehensive-case-training")));
when(service.taskAllocationMapper.selectByExample(any())).thenReturn(Collections.emptyList());
List<TrainingTask> result = service.listForStudent("stu-1", true);
assertEquals(List.of("new-product-survey"), result.stream()
.map(TrainingTask::getTaskKey)
.collect(java.util.stream.Collectors.toList()));
}
@Test
void studentTaskLookupHidesComprehensiveTrainingUntilTheClassHasPublishedIt() {
TrainingTaskServiceImpl service = studentTaskService("class-1");
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectByTaskKey("comprehensive-case-training")).thenReturn(comprehensive);
when(service.taskAllocationMapper.selectByExample(any())).thenReturn(Collections.emptyList());
TrainingTask result = service.getStudentTaskByTaskKey("comprehensive-case-training", "stu-1");
assertNull(result);
}
@Test
void studentTaskLookupReturnsComprehensiveTrainingWhenPublishedAndEnabledForTheClass() {
TrainingTaskServiceImpl service = studentTaskService("class-1");
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectByTaskKey("comprehensive-case-training")).thenReturn(comprehensive);
when(service.trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey("class-1", "comprehensive-case-training"))
.thenReturn(overrideTask("config-1", "class-1", "comprehensive-case-training"));
when(service.taskAllocationMapper.selectByExample(any())).thenReturn(List.of(
taskAllocation(TaskAllocation.PUBLICATION_MARKER, (byte) 1),
taskAllocation("comprehensive-case-training", (byte) 0)));
TrainingTask result = service.getStudentTaskByTaskKey("comprehensive-case-training", "stu-1");
assertEquals("comprehensive-case-training", result.getTaskKey());
}
@Test
void studentTaskLookupHidesPublishedComprehensiveTrainingWhenItsClassTaskIsDisabled() {
TrainingTaskServiceImpl service = studentTaskService("class-1");
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectByTaskKey("comprehensive-case-training")).thenReturn(comprehensive);
TrainingTaskClassConfig disabled = overrideTask("config-1", "class-1", "comprehensive-case-training");
disabled.setEnabled(Boolean.FALSE);
when(service.trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey("class-1", "comprehensive-case-training"))
.thenReturn(disabled);
when(service.taskAllocationMapper.selectByExample(any())).thenReturn(List.of(
taskAllocation(TaskAllocation.PUBLICATION_MARKER, (byte) 1),
taskAllocation("comprehensive-case-training", (byte) 0)));
TrainingTask result = service.getStudentTaskByTaskKey("comprehensive-case-training", "stu-1");
assertNull(result);
}
@Test
void studentListReturnsComprehensiveTrainingWhenPublishedAndEnabledForTheClass() {
TrainingTaskServiceImpl service = studentTaskService("class-1");
TrainingTask legacy = storedTask("task-1", "new-product-survey");
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(List.of(legacy, comprehensive));
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-1", null))
.thenReturn(List.of(overrideTask("config-1", "class-1", "new-product-survey"),
overrideTask("config-2", "class-1", "comprehensive-case-training")));
when(service.taskAllocationMapper.selectByExample(any())).thenReturn(List.of(
taskAllocation(TaskAllocation.PUBLICATION_MARKER, (byte) 1),
taskAllocation("new-product-survey", (byte) 0),
taskAllocation("comprehensive-case-training", (byte) 0)));
List<TrainingTask> result = service.listForStudent("stu-1", true);
assertEquals(List.of("new-product-survey", "comprehensive-case-training"), result.stream()
.map(TrainingTask::getTaskKey)
.collect(java.util.stream.Collectors.toList()));
}
@Test
void studentListHidesComprehensiveTrainingWhenThePublishedKeyIsRemoved() {
TrainingTaskServiceImpl service = studentTaskService("class-1");
TrainingTask legacy = storedTask("task-1", "new-product-survey");
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(List.of(legacy, comprehensive));
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-1", null))
.thenReturn(List.of(overrideTask("config-1", "class-1", "new-product-survey"),
overrideTask("config-2", "class-1", "comprehensive-case-training")));
when(service.taskAllocationMapper.selectByExample(any())).thenReturn(List.of(
taskAllocation(TaskAllocation.PUBLICATION_MARKER, (byte) 1),
taskAllocation("new-product-survey", (byte) 0)));
List<TrainingTask> result = service.listForStudent("stu-1", true);
assertEquals(List.of("new-product-survey"), result.stream()
.map(TrainingTask::getTaskKey)
.collect(java.util.stream.Collectors.toList()));
}
@Test
void studentListHidesComprehensiveTrainingWhenItsPublishedAllocationIsDisabled() {
TrainingTaskServiceImpl service = studentTaskService("class-1");
TrainingTask legacy = storedTask("task-1", "new-product-survey");
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(List.of(legacy, comprehensive));
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-1", null))
.thenReturn(List.of(overrideTask("config-1", "class-1", "new-product-survey"),
overrideTask("config-2", "class-1", "comprehensive-case-training")));
when(service.taskAllocationMapper.selectByExample(any())).thenReturn(List.of(
taskAllocation(TaskAllocation.PUBLICATION_MARKER, (byte) 1),
taskAllocation("new-product-survey", (byte) 0),
taskAllocation("comprehensive-case-training", (byte) 1)));
List<TrainingTask> result = service.listForStudent("stu-1", true);
assertEquals(List.of("new-product-survey"), result.stream()
.map(TrainingTask::getTaskKey)
.collect(java.util.stream.Collectors.toList()));
}
@Test
void studentListHidesDisabledComprehensiveTrainingWhenDisabledTasksAreRequested() {
TrainingTaskServiceImpl service = studentTaskService("class-1");
TrainingTask legacy = storedTask("task-1", "new-product-survey");
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(List.of(legacy, comprehensive));
TrainingTaskClassConfig disabled = overrideTask("config-2", "class-1", "comprehensive-case-training");
disabled.setEnabled(Boolean.FALSE);
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-1", null))
.thenReturn(List.of(overrideTask("config-1", "class-1", "new-product-survey"), disabled));
when(service.taskAllocationMapper.selectByExample(any())).thenReturn(List.of(
taskAllocation(TaskAllocation.PUBLICATION_MARKER, (byte) 1),
taskAllocation("new-product-survey", (byte) 0),
taskAllocation("comprehensive-case-training", (byte) 0)));
List<TrainingTask> result = service.listForStudent("stu-1", false);
assertEquals(List.of("new-product-survey"), result.stream()
.map(TrainingTask::getTaskKey)
.collect(java.util.stream.Collectors.toList()));
}
@Test
void teacherClassTaskListIncludesComprehensiveTrainingWithoutPublication() {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.schoolClassMapper = mock(SchoolClassMapper.class);
TrainingTask comprehensive = comprehensiveTrainingTask();
when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(List.of(comprehensive));
when(service.trainingTaskClassConfigMapper.selectListByTeachingClass("class-1", null)).thenReturn(Collections.emptyList());
SchoolClass schoolClass = teachingClass("class-1", "teacher-1");
schoolClass.setSchoolId("school-1");
when(service.schoolClassMapper.selectByPrimaryKey("class-1")).thenReturn(schoolClass);
JwtUser teacher = new JwtUser();
teacher.setUserId("teacher-1");
teacher.setSchoolId("school-1");
List<TrainingTask> result = service.listForTeachingClass("class-1", true, teacher);
assertEquals(List.of("comprehensive-case-training"), result.stream()
.map(TrainingTask::getTaskKey)
.collect(java.util.stream.Collectors.toList()));
}
private TrainingTaskServiceImpl studentTaskService(String teachingClassId) {
TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();
service.trainingTaskMapper = mock(TrainingTaskMapper.class);
service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class);
service.teachingClassStudentMapper = mock(TeachingClassStudentMapper.class);
service.taskAllocationMapper = mock(TaskAllocationMapper.class);
TeachingClassStudent membership = new TeachingClassStudent();
membership.setTeachingClassId(teachingClassId);
when(service.teachingClassStudentMapper.selectActiveByStudentUserId("stu-1")).thenReturn(membership);
return service;
}
private TrainingTask comprehensiveTrainingTask() {
TrainingTask task = storedTask("task-comprehensive", "comprehensive-case-training");
task.setProjectName("综合实训");
task.setTaskName("综合案例实训");
task.setSort(1);
return task;
}
private TaskAllocation taskAllocation(String module, byte disabledStatus) {
TaskAllocation allocation = new TaskAllocation();
allocation.setModule(module);
allocation.setDisabledStatus(disabledStatus);
return allocation;
}
private TrainingTask storedTask(String id, String taskKey) {
TrainingTask task = new TrainingTask();
task.setId(id);
task.setTaskKey(taskKey);
task.setProjectName("Foundation");
task.setTaskName("Original Name");
task.setSteps("[\"Step 1\",\"Step 2\",\"Step 3\",\"Step 4\"]");
task.setSort(7);
task.setEnabled(Boolean.TRUE);
return task;
}
private SchoolClass teachingClass(String classId, String createdBy) {
SchoolClass schoolClass = new SchoolClass();
schoolClass.setSchoolClassId(classId);
schoolClass.setClassType("TEACHING");
schoolClass.setCreatedBy(createdBy);
return schoolClass;
}
private TrainingTaskClassConfig overrideTask(String id, String teachingClassId, String taskKey) {
TrainingTaskClassConfig config = new TrainingTaskClassConfig();
config.setId(id);
config.setTeachingClassId(teachingClassId);
config.setTaskKey(taskKey);
config.setProjectName("Foundation");
config.setTaskName("Override Name");
config.setSteps("[\"Step 1\",\"Step 2\",\"Step 3\",\"Step 4\"]");
config.setSort(7);
config.setEnabled(Boolean.TRUE);
return config;
}
}