package com.sztzjy.linkCommerce.service.impl; import com.sztzjy.linkCommerce.entity.SchoolClass; 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.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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class TrainingTaskServiceImplTest { @Test void splitListSupportsCommonSeparators() { TrainingTaskServiceImpl service = new TrainingTaskServiceImpl(); List 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 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 errors = new ArrayList<>(); TrainingTask task = service.buildTask(row, 2, errors); assertEquals(0, errors.size()); assertEquals("Teacher Edited Survey", task.getTaskName()); assertEquals("[\"s1\",\"s2\",\"Step 3\",\"Step 4\"]", task.getSteps()); } @Test void importTaskRejectsUnknownTaskKeyBecauseTaskCountIsFixed() { TrainingTaskServiceImpl service = new TrainingTaskServiceImpl(); TrainingTaskImportDTO row = new TrainingTaskImportDTO(); row.setProjectName("Custom"); row.setTaskKey("custom-task"); row.setTaskName("Custom Task"); List errors = new ArrayList<>(); TrainingTask task = service.buildTask(row, 2, errors); assertEquals(null, task); assertFalse(errors.isEmpty()); } @Test void updateTaskPersistsTeacherEditedTaskNameAndFixedStepCount() { 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 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()); assertEquals("[\"Only One\",\"Step 2\",\"Step 3\",\"Step 4\"]", captor.getValue().getSteps()); } @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 errors = new ArrayList<>(); int count = service.importTasks(Collections.singletonList(row), "teacher-1", errors); assertEquals(1, count); assertEquals(0, errors.size()); ArgumentCaptor captor = ArgumentCaptor.forClass(TrainingTask.class); verify(service.trainingTaskMapper).updateByPrimaryKeySelective(captor.capture()); assertEquals("task-1", captor.getValue().getId()); assertEquals("Teacher Edited Survey", captor.getValue().getTaskName()); assertEquals("[\"s1\",\"s2\",\"s3\",\"s4\"]", captor.getValue().getSteps()); } @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 captor = ArgumentCaptor.forClass(TrainingTask.class); verify(service.trainingTaskMapper, times(22)).insertSelective(captor.capture()); assertFalse(captor.getAllValues().stream().anyMatch(task -> "new-product-survey".equals(task.getTaskKey()))); assertEquals("product-development-factors", captor.getAllValues().get(0).getTaskKey()); } @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 listDoesNotFilterDisabledTasksWhenEnabledOnlyIsFalse() { TrainingTaskServiceImpl service = new TrainingTaskServiceImpl(); service.trainingTaskMapper = mock(TrainingTaskMapper.class); when(service.trainingTaskMapper.selectByTaskKey(any())).thenReturn(new TrainingTask()); service.list(null, null, false); verify(service.trainingTaskMapper).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 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 captor = ArgumentCaptor.forClass(TrainingTaskClassConfig.class); verify(service.trainingTaskClassConfigMapper).insertSelective(captor.capture()); assertEquals("class-1", captor.getValue().getTeachingClassId()); assertEquals("new-product-survey", captor.getValue().getTaskKey()); assertEquals("Default Project", captor.getValue().getProjectName()); assertEquals(Integer.valueOf(3), captor.getValue().getSort()); assertEquals("Class Knowledge", captor.getValue().getKnowledge()); assertEquals("Class Material", captor.getValue().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 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"); 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)); List result = service.listForTeachingClass("class-1", true, "teacher-1"); assertEquals(0, result.size()); } 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 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; } }