diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 2eef803..f30eef9 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,16 +1,15 @@ -name: Spring Boot Student Management App CI/CD +name: Spring Boot CI/CD on: push: - branches: [ "main", "dev" ] + branches: [ "dev", "feature/*", "fix/*" ] pull_request: - branches: [ "main" ] + branches: [ "main", "dev" ] jobs: build-and-test: runs-on: ubuntu-latest - # Spin up Postgres for integration tests services: postgres: image: postgres:latest @@ -27,33 +26,19 @@ jobs: --health-retries 5 steps: - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Set up JDK 21 (matches your pom.xml) + - name: Setup Java 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' cache: maven - - name: Make Maven Wrapper executable - run: chmod +x mvnw - - - name: Run Tests with Maven Wrapper - run: ./mvnw clean test + - name: Test & Build + run: ./mvnw clean verify env: - # Match your application.properties/docker-compose settings SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/smsdb_test SPRING_DATASOURCE_USERNAME: admin SPRING_DATASOURCE_PASSWORD: pass123 - SPRING_JPA_HIBERNATE_DDL_AUTO: update - SPRING_JPA_SHOW_SQL: false - - - name: Build JAR with Maven Wrapper - run: ./mvnw clean package -DskipTests - - - name: Verify JAR file - run: | - ls -la target/*.jar - echo "Build completed successfully!" \ No newline at end of file + SPRING_JPA_HIBERNATE_DDL_AUTO: update \ No newline at end of file diff --git a/compose.yaml b/compose.yaml index 5730ec9..df70033 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,7 +1,7 @@ services: postgres: image: 'postgres:latest' - container_name: sms_DB + container_name: sms_db environment: - 'POSTGRES_DB=smsdb' - 'POSTGRES_PASSWORD=pass123' @@ -10,7 +10,6 @@ services: - '5432:5432' volumes: - postgres_data:/var/lib/postgresql - - ./init.sql:/docker-entrypoint-initdb.d/init.sql networks: - student-network healthcheck: diff --git a/pom.xml b/pom.xml index d9c96fd..c3c4db7 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,11 @@ postgresql runtime + + com.h2database + h2 + test + diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/config/SecurityConfig.java b/src/main/java/com/shuvocse21/StudentManagementApp/config/SecurityConfig.java index a4eb9cd..aaa74f2 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/config/SecurityConfig.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/config/SecurityConfig.java @@ -26,14 +26,14 @@ public PasswordEncoder passwordEncoder() { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth - .requestMatchers("/", "/login", "/register/teacher/**").permitAll() + .requestMatchers("/", "/login", "/register/teacher/**", "/css/**").permitAll() .requestMatchers("/student/**").hasRole("STUDENT") .requestMatchers("/teacher/**").hasRole("TEACHER") .anyRequest().authenticated() ) .formLogin(form -> form .loginPage("/login") - .defaultSuccessUrl("/dashboard") + .defaultSuccessUrl("/dashboard", true) .permitAll() ) .logout(logout -> logout diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/controller/AuthController.java b/src/main/java/com/shuvocse21/StudentManagementApp/controller/AuthController.java index 045cdb2..28c2cd1 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/controller/AuthController.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/controller/AuthController.java @@ -1,22 +1,20 @@ package com.shuvocse21.StudentManagementApp.controller; -import com.shuvocse21.StudentManagementApp.entity.Department; import com.shuvocse21.StudentManagementApp.service.UserService; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.mvc.support.RedirectAttributes; -import java.util.List; @Controller @RequiredArgsConstructor public class AuthController { private final UserService userService; - private final com.shuvocse21.StudentManagementApp.repository.DepartmentRepository departmentRepository; @GetMapping("/") public String home() { @@ -30,33 +28,34 @@ public String login() { @GetMapping("/dashboard") public String dashboard() { - return "dashboard"; + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getPrincipal())) { + String role = auth.getAuthorities().iterator().next().getAuthority(); + if (role.equals("ROLE_STUDENT")) { + return "redirect:/student/dashboard"; + } else if (role.equals("ROLE_TEACHER")) { + return "redirect:/teacher/dashboard"; + } + } + return "redirect:/login"; } - // TEACHER REGISTRATION ENDPOINTS @GetMapping("/register/teacher") - public String registerTeacher(Model model) { - List departments = departmentRepository.findAll(); - model.addAttribute("departments", departments); + public String registerTeacher() { return "register-teacher"; } @PostMapping("/register/teacher") - public String registerTeacherUser( - @RequestParam String username, - @RequestParam String password, - @RequestParam String email, - @RequestParam String employeeId, - @RequestParam Long departmentId, - RedirectAttributes redirectAttributes) { - + public String registerTeacher(@RequestParam String username, @RequestParam String password, + @RequestParam String email, @RequestParam String employeeId, + RedirectAttributes redirectAttributes) { try { - userService.registerTeacher(username, password, email, employeeId, departmentId); - redirectAttributes.addFlashAttribute("success", "Teacher registration successful! Please login."); - return "redirect:/login"; + userService.registerTeacher(username, password, email, employeeId); + redirectAttributes.addFlashAttribute("success", "Registration successful! Please login."); } catch (RuntimeException e) { redirectAttributes.addFlashAttribute("error", e.getMessage()); return "redirect:/register/teacher"; } + return "redirect:/login"; } } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/controller/StudentController.java b/src/main/java/com/shuvocse21/StudentManagementApp/controller/StudentController.java index 46850ed..f638cf5 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/controller/StudentController.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/controller/StudentController.java @@ -1,5 +1,6 @@ package com.shuvocse21.StudentManagementApp.controller; +import com.shuvocse21.StudentManagementApp.dto.StudentDTO; import com.shuvocse21.StudentManagementApp.entity.Student; import com.shuvocse21.StudentManagementApp.entity.User; import com.shuvocse21.StudentManagementApp.service.UserService; @@ -19,52 +20,33 @@ public class StudentController { private final UserService userService; private User getCurrentUser() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && authentication.isAuthenticated() && - !"anonymousUser".equals(authentication.getPrincipal())) { - String username = authentication.getName(); - return userService.getUserByUsername(username); + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getPrincipal())) { + return userService.getUserByUsername(auth.getName()); } return null; } - @GetMapping("/profile") - public String profile(Model model) { + @GetMapping("/dashboard") + public String dashboard(Model model) { User user = getCurrentUser(); - if (user == null) { - return "redirect:/login"; - } + if (user == null) return "redirect:/login"; - Student student = userService.getStudentByUserId(user.getId()); - model.addAttribute("student", student); - return "student/profile"; + StudentDTO studentDTO = userService.getStudentDTOByUserId(user.getId()); + model.addAttribute("student", studentDTO); + return "student/dashboard"; } @PostMapping("/profile/update") - public String updateProfile( - @RequestParam String email, - @RequestParam String phone, - @RequestParam String address, - RedirectAttributes redirectAttributes) { - + public String updateProfile(@RequestParam String email, @RequestParam String phone, + @RequestParam String address, RedirectAttributes redirectAttributes) { User user = getCurrentUser(); - if (user == null) { - return "redirect:/login"; - } + if (user == null) return "redirect:/login"; + // FIXED: Using getStudentByUserId method which now exists Student student = userService.getStudentByUserId(user.getId()); - - // Update email in User entity - student.getUser().setEmail(email); - - // Update phone and address in Student entity - student.setPhone(phone); - student.setAddress(address); - - // Save the changes through service - userService.updateStudent(student.getId(), email, phone, address, student.getDepartment().getId()); - + userService.updateStudent(student.getId(), email, phone, address); redirectAttributes.addFlashAttribute("success", "Profile updated successfully!"); - return "redirect:/student/profile"; + return "redirect:/student/dashboard"; } } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/controller/TeacherController.java b/src/main/java/com/shuvocse21/StudentManagementApp/controller/TeacherController.java index c1c28fc..1b24492 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/controller/TeacherController.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/controller/TeacherController.java @@ -1,7 +1,10 @@ package com.shuvocse21.StudentManagementApp.controller; +import com.shuvocse21.StudentManagementApp.dto.CourseDTO; +import com.shuvocse21.StudentManagementApp.dto.StudentDTO; import com.shuvocse21.StudentManagementApp.entity.*; import com.shuvocse21.StudentManagementApp.service.UserService; +import com.shuvocse21.StudentManagementApp.repository.*; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -9,7 +12,6 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes; import java.util.List; -import java.util.stream.Collectors; @Controller @RequestMapping("/teacher") @@ -17,70 +19,57 @@ public class TeacherController { private final UserService userService; - private final com.shuvocse21.StudentManagementApp.repository.DepartmentRepository departmentRepository; - private final com.shuvocse21.StudentManagementApp.repository.CourseRepository courseRepository; - private final com.shuvocse21.StudentManagementApp.repository.TeacherRepository teacherRepository; - private final com.shuvocse21.StudentManagementApp.repository.StudentRepository studentRepository; + private final CourseRepository courseRepository; + private final StudentRepository studentRepository; @GetMapping("/dashboard") - public String dashboard() { + public String dashboard(Model model) { + List courses = userService.getAllCourseDTOs(); + List students = userService.getAllStudentDTOs(); + model.addAttribute("courses", courses); + model.addAttribute("students", students); + model.addAttribute("totalStudents", students.size()); + model.addAttribute("totalCourses", courses.size()); return "teacher/dashboard"; } @GetMapping("/students") public String viewStudents(Model model) { - List students = userService.getAllStudents(); - model.addAttribute("students", students); + model.addAttribute("students", userService.getAllStudentDTOs()); return "teacher/students"; } @GetMapping("/add-student") - public String showAddStudentForm(Model model) { - List departments = departmentRepository.findAll(); - model.addAttribute("departments", departments); + public String showAddStudentForm() { return "teacher/add-student"; } @PostMapping("/add-student") - public String addStudent( - @RequestParam String username, - @RequestParam String password, - @RequestParam String email, - @RequestParam String studentId, - @RequestParam String phone, - @RequestParam String address, - @RequestParam Long departmentId, - RedirectAttributes redirectAttributes) { - + public String addStudent(@RequestParam String username, @RequestParam String password, + @RequestParam String email, @RequestParam String studentId, + @RequestParam String phone, @RequestParam String address, + RedirectAttributes redirectAttributes) { try { - userService.registerStudent(username, password, email, studentId, phone, address, departmentId); + userService.registerStudent(username, password, email, studentId, phone, address); redirectAttributes.addFlashAttribute("success", "Student added successfully!"); - return "redirect:/teacher/students"; } catch (RuntimeException e) { redirectAttributes.addFlashAttribute("error", e.getMessage()); - return "redirect:/teacher/add-student"; } + return "redirect:/teacher/students"; } @GetMapping("/edit-student/{id}") public String editStudentForm(@PathVariable Long id, Model model) { Student student = userService.getStudentById(id); - List departments = departmentRepository.findAll(); model.addAttribute("student", student); - model.addAttribute("departments", departments); return "teacher/edit-student"; } @PostMapping("/update-student/{id}") - public String updateStudent( - @PathVariable Long id, - @RequestParam String email, - @RequestParam String phone, - @RequestParam String address, - @RequestParam Long departmentId, - RedirectAttributes redirectAttributes) { - - userService.updateStudent(id, email, phone, address, departmentId); + public String updateStudent(@PathVariable Long id, @RequestParam String email, + @RequestParam String phone, @RequestParam String address, + RedirectAttributes redirectAttributes) { + userService.updateStudent(id, email, phone, address); redirectAttributes.addFlashAttribute("success", "Student updated successfully!"); return "redirect:/teacher/students"; } @@ -92,230 +81,60 @@ public String deleteStudent(@PathVariable Long id, RedirectAttributes redirectAt return "redirect:/teacher/students"; } - // Course Management @GetMapping("/courses") public String viewCourses(Model model) { - List courses = courseRepository.findAll(); - model.addAttribute("courses", courses); + model.addAttribute("courses", userService.getAllCourseDTOs()); return "teacher/courses"; } @GetMapping("/add-course") - public String showAddCourseForm(Model model) { - List departments = departmentRepository.findAll(); - model.addAttribute("departments", departments); + public String showAddCourseForm() { return "teacher/add-course"; } @PostMapping("/add-course") - public String addCourse( - @RequestParam String name, - @RequestParam String code, - @RequestParam Long departmentId, - RedirectAttributes redirectAttributes) { - + public String addCourse(@RequestParam String name, @RequestParam String code, + RedirectAttributes redirectAttributes) { try { Course course = new Course(); course.setName(name); course.setCode(code); - - Department department = departmentRepository.findById(departmentId) - .orElseThrow(() -> new RuntimeException("Department not found")); - course.setDepartment(department); - courseRepository.save(course); redirectAttributes.addFlashAttribute("success", "Course added successfully!"); - return "redirect:/teacher/courses"; - } catch (RuntimeException e) { - redirectAttributes.addFlashAttribute("error", e.getMessage()); - return "redirect:/teacher/add-course"; - } - } - - // Department Management - @GetMapping("/departments") - public String viewDepartments(Model model) { - List departments = departmentRepository.findAll(); - model.addAttribute("departments", departments); - return "teacher/departments"; - } - - @GetMapping("/add-department") - public String showAddDepartmentForm() { - return "teacher/add-department"; - } - - @PostMapping("/add-department") - public String addDepartment( - @RequestParam String name, - @RequestParam String code, - RedirectAttributes redirectAttributes) { - - try { - Department department = new Department(); - department.setName(name); - department.setCode(code); - - departmentRepository.save(department); - redirectAttributes.addFlashAttribute("success", "Department added successfully!"); - return "redirect:/teacher/departments"; } catch (RuntimeException e) { redirectAttributes.addFlashAttribute("error", e.getMessage()); - return "redirect:/teacher/add-department"; } + return "redirect:/teacher/courses"; } - // Course Enrollment Management @GetMapping("/enroll-student") public String showEnrollStudentForm(Model model) { - List students = userService.getAllStudents(); - List courses = courseRepository.findAll(); - - model.addAttribute("students", students); - model.addAttribute("courses", courses); + model.addAttribute("students", userService.getAllStudentDTOs()); + model.addAttribute("courses", userService.getAllCourseDTOs()); return "teacher/enroll-student"; } @PostMapping("/enroll-student") - public String enrollStudentToCourse( - @RequestParam Long studentId, - @RequestParam Long courseId, - RedirectAttributes redirectAttributes) { - - try { - Student student = studentRepository.findById(studentId) - .orElseThrow(() -> new RuntimeException("Student not found")); - - Course course = courseRepository.findById(courseId) - .orElseThrow(() -> new RuntimeException("Course not found")); - - // Check if already enrolled - if (student.getCourses().contains(course)) { - redirectAttributes.addFlashAttribute("error", "Student is already enrolled in this course!"); - return "redirect:/teacher/enroll-student"; - } - - // Enroll student in course - student.getCourses().add(course); - studentRepository.save(student); - - redirectAttributes.addFlashAttribute("success", - "Student " + student.getStudentId() + " successfully enrolled in " + course.getCode() + "!"); - return "redirect:/teacher/enroll-student"; - } catch (RuntimeException e) { - redirectAttributes.addFlashAttribute("error", e.getMessage()); - return "redirect:/teacher/enroll-student"; - } - } - - @GetMapping("/view-student-courses/{studentId}") - public String viewStudentCourses(@PathVariable Long studentId, Model model) { - Student student = studentRepository.findById(studentId) - .orElseThrow(() -> new RuntimeException("Student not found")); - - List allCourses = courseRepository.findAll(); - - // Filter out courses the student is already enrolled in - List availableCourses = allCourses.stream() - .filter(course -> !student.getCourses().contains(course)) - .collect(Collectors.toList()); - - model.addAttribute("student", student); - model.addAttribute("availableCourses", availableCourses); - return "teacher/manage-student-courses"; - } - - @PostMapping("/enroll-student-course/{studentId}") - public String enrollStudentInCourse( - @PathVariable Long studentId, - @RequestParam Long courseId, - RedirectAttributes redirectAttributes) { - - try { - Student student = studentRepository.findById(studentId) - .orElseThrow(() -> new RuntimeException("Student not found")); - - Course course = courseRepository.findById(courseId) - .orElseThrow(() -> new RuntimeException("Course not found")); - - // Check if already enrolled - if (student.getCourses().contains(course)) { - redirectAttributes.addFlashAttribute("error", "Student is already enrolled in this course!"); - return "redirect:/teacher/view-student-courses/" + studentId; - } - - // Enroll student in course - student.getCourses().add(course); - studentRepository.save(student); - - redirectAttributes.addFlashAttribute("success", - "Successfully enrolled in " + course.getCode() + "!"); - return "redirect:/teacher/view-student-courses/" + studentId; - } catch (RuntimeException e) { - redirectAttributes.addFlashAttribute("error", e.getMessage()); - return "redirect:/teacher/view-student-courses/" + studentId; - } - } - - @GetMapping("/remove-course-from-student/{studentId}/{courseId}") - public String removeCourseFromStudent( - @PathVariable Long studentId, - @PathVariable Long courseId, - RedirectAttributes redirectAttributes) { - + public String enrollStudent(@RequestParam Long studentId, @RequestParam Long courseId, + RedirectAttributes redirectAttributes) { try { - Student student = studentRepository.findById(studentId) - .orElseThrow(() -> new RuntimeException("Student not found")); - - Course course = courseRepository.findById(courseId) - .orElseThrow(() -> new RuntimeException("Course not found")); - - // Remove course from student's courses - student.getCourses().remove(course); - studentRepository.save(student); - - redirectAttributes.addFlashAttribute("success", - "Successfully removed from " + course.getCode() + "!"); - return "redirect:/teacher/view-student-courses/" + studentId; + userService.enrollStudentInCourse(studentId, courseId); + redirectAttributes.addFlashAttribute("success", "Student enrolled successfully!"); } catch (RuntimeException e) { redirectAttributes.addFlashAttribute("error", e.getMessage()); - return "redirect:/teacher/view-student-courses/" + studentId; } + return "redirect:/teacher/enroll-student"; } - @GetMapping("/view-course-students/{courseId}") - public String viewCourseStudents(@PathVariable Long courseId, Model model) { - Course course = courseRepository.findById(courseId) - .orElseThrow(() -> new RuntimeException("Course not found")); - - model.addAttribute("course", course); - return "teacher/view-course-students"; - } - - // Fixed: This is the second removeStudentFromCourse method - kept original name - @GetMapping("/remove-student-from-course/{courseId}/{studentId}") - public String removeStudentFromCourse( - @PathVariable Long courseId, - @PathVariable Long studentId, - RedirectAttributes redirectAttributes) { - + @GetMapping("/remove-enrollment/{studentId}/{courseId}") + public String removeEnrollment(@PathVariable Long studentId, @PathVariable Long courseId, + RedirectAttributes redirectAttributes) { try { - Course course = courseRepository.findById(courseId) - .orElseThrow(() -> new RuntimeException("Course not found")); - - Student student = studentRepository.findById(studentId) - .orElseThrow(() -> new RuntimeException("Student not found")); - - // Remove student from course - student.getCourses().remove(course); - studentRepository.save(student); - - redirectAttributes.addFlashAttribute("success", - "Student " + student.getStudentId() + " removed from course!"); - return "redirect:/teacher/view-course-students/" + courseId; + userService.removeStudentFromCourse(studentId, courseId); + redirectAttributes.addFlashAttribute("success", "Enrollment removed successfully!"); } catch (RuntimeException e) { redirectAttributes.addFlashAttribute("error", e.getMessage()); - return "redirect:/teacher/view-course-students/" + courseId; } + return "redirect:/teacher/enroll-student"; } } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/dto/CourseDTO.java b/src/main/java/com/shuvocse21/StudentManagementApp/dto/CourseDTO.java index b21c5eb..74307f0 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/dto/CourseDTO.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/dto/CourseDTO.java @@ -7,8 +7,6 @@ public class CourseDTO { private Long id; private String name; private String code; - private Long departmentId; - private String departmentName; - private Long teacherId; private String teacherName; + private int studentCount; } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/dto/StudentDTO.java b/src/main/java/com/shuvocse21/StudentManagementApp/dto/StudentDTO.java index a0cfeda..20d13cf 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/dto/StudentDTO.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/dto/StudentDTO.java @@ -1,18 +1,15 @@ package com.shuvocse21.StudentManagementApp.dto; import lombok.Data; -import java.time.LocalDate; +import java.util.List; @Data public class StudentDTO { private Long id; - private String firstName; - private String lastName; - private String studentId; + private String username; private String email; + private String studentId; private String phone; private String address; - private LocalDate dateOfBirth; - private String departmentName; - private Long departmentId; + private List courseCodes; } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/dto/UserRegistrationDTO.java b/src/main/java/com/shuvocse21/StudentManagementApp/dto/UserRegistrationDTO.java index e93987d..cfe8664 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/dto/UserRegistrationDTO.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/dto/UserRegistrationDTO.java @@ -6,14 +6,10 @@ public class UserRegistrationDTO { private String username; private String password; - private String confirmPassword; private String email; private String role; - private String firstName; - private String lastName; private String studentId; private String employeeId; private String phone; - private Long departmentId; - + private String address; } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/entity/Course.java b/src/main/java/com/shuvocse21/StudentManagementApp/entity/Course.java index cb725eb..e8c79cb 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/entity/Course.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/entity/Course.java @@ -23,10 +23,6 @@ public class Course { @JoinColumn(name = "teacher_id") private Teacher teacher; - @ManyToOne - @JoinColumn(name = "department_id") - private Department department; - @ManyToMany(mappedBy = "courses") private List students = new ArrayList<>(); } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/entity/Department.java b/src/main/java/com/shuvocse21/StudentManagementApp/entity/Department.java deleted file mode 100644 index d2dbaf6..0000000 --- a/src/main/java/com/shuvocse21/StudentManagementApp/entity/Department.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.shuvocse21.StudentManagementApp.entity; - -import jakarta.persistence.*; -import lombok.Data; - -@Entity -@Table(name = "departments") -@Data -public class Department { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(unique = true, nullable = false) - private String name; - - @Column(unique = true, nullable = false) - private String code; -} \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/entity/Student.java b/src/main/java/com/shuvocse21/StudentManagementApp/entity/Student.java index ac3d96c..c36f430 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/entity/Student.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/entity/Student.java @@ -17,10 +17,6 @@ public class Student { @JoinColumn(name = "user_id", unique = true) private User user; - @ManyToOne - @JoinColumn(name = "department_id") - private Department department; - @Column(unique = true, nullable = false) private String studentId; diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/entity/Teacher.java b/src/main/java/com/shuvocse21/StudentManagementApp/entity/Teacher.java index a5cef6e..d431fd1 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/entity/Teacher.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/entity/Teacher.java @@ -15,10 +15,6 @@ public class Teacher { @JoinColumn(name = "user_id", unique = true) private User user; - @ManyToOne - @JoinColumn(name = "department_id") - private Department department; - @Column(unique = true, nullable = false) private String employeeId; } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/entity/User.java b/src/main/java/com/shuvocse21/StudentManagementApp/entity/User.java index a1e4f03..5ff6270 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/entity/User.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/entity/User.java @@ -21,7 +21,7 @@ public class User { private String email; @Column(nullable = false) - private String role; // STUDENT, TEACHER + private String role; private boolean enabled = true; } \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/repository/DepartmentRepository.java b/src/main/java/com/shuvocse21/StudentManagementApp/repository/DepartmentRepository.java deleted file mode 100644 index 678c2fd..0000000 --- a/src/main/java/com/shuvocse21/StudentManagementApp/repository/DepartmentRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.shuvocse21.StudentManagementApp.repository; - -import com.shuvocse21.StudentManagementApp.entity.Department; -import org.springframework.data.jpa.repository.JpaRepository; -import java.util.Optional; - -public interface DepartmentRepository extends JpaRepository { - Optional findByCode(String code); -} \ No newline at end of file diff --git a/src/main/java/com/shuvocse21/StudentManagementApp/service/UserService.java b/src/main/java/com/shuvocse21/StudentManagementApp/service/UserService.java index 728566e..26a79a1 100644 --- a/src/main/java/com/shuvocse21/StudentManagementApp/service/UserService.java +++ b/src/main/java/com/shuvocse21/StudentManagementApp/service/UserService.java @@ -1,13 +1,17 @@ package com.shuvocse21.StudentManagementApp.service; +import com.shuvocse21.StudentManagementApp.dto.CourseDTO; +import com.shuvocse21.StudentManagementApp.dto.StudentDTO; import com.shuvocse21.StudentManagementApp.entity.*; import com.shuvocse21.StudentManagementApp.repository.*; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.modelmapper.ModelMapper; import java.util.List; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -16,64 +20,53 @@ public class UserService { private final UserRepository userRepository; private final StudentRepository studentRepository; private final TeacherRepository teacherRepository; - private final DepartmentRepository departmentRepository; private final CourseRepository courseRepository; private final PasswordEncoder passwordEncoder; + private final ModelMapper modelMapper; @Transactional public User registerStudent(String username, String password, String email, - String studentId, String phone, String address, Long departmentId) { + String studentId, String phone, String address) { if (userRepository.existsByUsername(username)) { throw new RuntimeException("Username already exists"); } - if (userRepository.existsByEmail(email)) { throw new RuntimeException("Email already exists"); } - if (studentRepository.existsByStudentId(studentId)) { throw new RuntimeException("Student ID already exists"); } - Department department = departmentRepository.findById(departmentId) - .orElseThrow(() -> new RuntimeException("Department not found")); - User user = new User(); user.setUsername(username); user.setPassword(passwordEncoder.encode(password)); user.setEmail(email); user.setRole("STUDENT"); user.setEnabled(true); - User savedUser = userRepository.save(user); Student student = new Student(); student.setUser(savedUser); - student.setDepartment(department); student.setStudentId(studentId); student.setPhone(phone); student.setAddress(address); - studentRepository.save(student); return savedUser; } @Transactional - public User registerTeacher(String username, String password, String email, - String employeeId, Long departmentId) { - + public User registerTeacher(String username, String password, String email, String employeeId) { if (userRepository.existsByUsername(username)) { throw new RuntimeException("Username already exists"); } - if (userRepository.existsByEmail(email)) { throw new RuntimeException("Email already exists"); } - - Department department = departmentRepository.findById(departmentId) - .orElseThrow(() -> new RuntimeException("Department not found")); + if (teacherRepository.existsByEmployeeId(employeeId)) { + throw new RuntimeException("Employee ID already exists"); + } User user = new User(); user.setUsername(username); @@ -81,53 +74,76 @@ public User registerTeacher(String username, String password, String email, user.setEmail(email); user.setRole("TEACHER"); user.setEnabled(true); - User savedUser = userRepository.save(user); Teacher teacher = new Teacher(); teacher.setUser(savedUser); - teacher.setDepartment(department); teacher.setEmployeeId(employeeId); - teacherRepository.save(teacher); return savedUser; } - // Add this method to get user by username public User getUserByUsername(String username) { return userRepository.findByUsername(username) .orElseThrow(() -> new RuntimeException("User not found")); } + // FIXED: Added this missing method public Student getStudentByUserId(Long userId) { return studentRepository.findByUserId(userId) .orElseThrow(() -> new RuntimeException("Student not found")); } - public Student getStudentById(Long id) { - return studentRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Student not found")); + public StudentDTO getStudentDTOByUserId(Long userId) { + Student student = getStudentByUserId(userId); + return convertToStudentDTO(student); } - public List getAllStudents() { - return studentRepository.findAll(); + public List getAllStudentDTOs() { + return studentRepository.findAll().stream() + .map(this::convertToStudentDTO) + .collect(Collectors.toList()); + } + + public List getAllCourseDTOs() { + return courseRepository.findAll().stream() + .map(this::convertToCourseDTO) + .collect(Collectors.toList()); + } + + private StudentDTO convertToStudentDTO(Student student) { + StudentDTO dto = new StudentDTO(); + dto.setId(student.getId()); + dto.setUsername(student.getUser().getUsername()); + dto.setEmail(student.getUser().getEmail()); + dto.setStudentId(student.getStudentId()); + dto.setPhone(student.getPhone()); + dto.setAddress(student.getAddress()); + dto.setCourseCodes(student.getCourses().stream() + .map(Course::getCode) + .collect(Collectors.toList())); + return dto; + } + + private CourseDTO convertToCourseDTO(Course course) { + CourseDTO dto = new CourseDTO(); + dto.setId(course.getId()); + dto.setName(course.getName()); + dto.setCode(course.getCode()); + dto.setTeacherName(course.getTeacher() != null ? + course.getTeacher().getUser().getUsername() : "Not Assigned"); + dto.setStudentCount(course.getStudents().size()); + return dto; } @Transactional - public void updateStudent(Long studentId, String email, String phone, - String address, Long departmentId) { + public void updateStudent(Long studentId, String email, String phone, String address) { Student student = studentRepository.findById(studentId) .orElseThrow(() -> new RuntimeException("Student not found")); - - Department department = departmentRepository.findById(departmentId) - .orElseThrow(() -> new RuntimeException("Department not found")); - student.getUser().setEmail(email); student.setPhone(phone); student.setAddress(address); - student.setDepartment(department); - userRepository.save(student.getUser()); studentRepository.save(student); } @@ -136,7 +152,6 @@ public void updateStudent(Long studentId, String email, String phone, public void deleteStudent(Long studentId) { Student student = studentRepository.findById(studentId) .orElseThrow(() -> new RuntimeException("Student not found")); - User user = student.getUser(); studentRepository.delete(student); userRepository.delete(user); @@ -146,39 +161,37 @@ public void deleteStudent(Long studentId) { public void enrollStudentInCourse(Long studentId, Long courseId) { Student student = studentRepository.findById(studentId) .orElseThrow(() -> new RuntimeException("Student not found")); - Course course = courseRepository.findById(courseId) .orElseThrow(() -> new RuntimeException("Course not found")); - // Check if student is already enrolled - if (student.getCourses().contains(course)) { - throw new RuntimeException("Student is already enrolled in this course"); + if (!student.getCourses().contains(course)) { + student.getCourses().add(course); + studentRepository.save(student); } - - // Enroll student in course - student.getCourses().add(course); - studentRepository.save(student); } @Transactional public void removeStudentFromCourse(Long studentId, Long courseId) { Student student = studentRepository.findById(studentId) .orElseThrow(() -> new RuntimeException("Student not found")); - Course course = courseRepository.findById(courseId) .orElseThrow(() -> new RuntimeException("Course not found")); - // Remove course from student's courses student.getCourses().remove(course); studentRepository.save(student); } - public List getStudentsByCourseId(Long courseId) { - Course course = courseRepository.findById(courseId) - .orElseThrow(() -> new RuntimeException("Course not found")); - return course.getStudents(); + public Student getStudentById(Long id) { + return studentRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Student not found")); } -} - + public Course getCourseById(Long id) { + return courseRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Course not found")); + } + public List getAllCourses() { + return courseRepository.findAll(); + } +} \ No newline at end of file diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html deleted file mode 100644 index 1b9c4e9..0000000 --- a/src/main/resources/templates/dashboard.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - Dashboard - - -

Welcome to Student Management System

- -
-

Logged in as:

-

Role:

- - - - - -
- -
-
- -
-

Login

-

Are you a teacher? Register as Teacher

-
- - \ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html new file mode 100644 index 0000000..624ffac --- /dev/null +++ b/src/main/resources/templates/fragments/navbar.html @@ -0,0 +1,31 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 6707ce2..d485afe 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -3,22 +3,43 @@ Login + - -

Login

-
Invalid username or password
-
+ +
+
+
+
+
+

Student Management System

+
+
+
+ Invalid username or password +
+
-
-
- - -
-
- - + +
+ + +
+
+ + +
+ + + +
+

New Teacher? Register here

+
+
+
+
- - +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/register-teacher.html b/src/main/resources/templates/register-teacher.html index 53ef361..6b91b77 100644 --- a/src/main/resources/templates/register-teacher.html +++ b/src/main/resources/templates/register-teacher.html @@ -2,45 +2,49 @@ - Register as Teacher + Register Teacher + - -

Register as Teacher

+ +
+
+
+
+
+

Teacher Registration

+
+
+
-
-
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
- -
-
- - +
+

Already have an account? Login here

+
+
+
+
-
- - -
-
- - -
-
- - -
-
- - -
- - +
-

Already have an account? Login here

-Back to Dashboard + \ No newline at end of file diff --git a/src/main/resources/templates/student/dashboard.html b/src/main/resources/templates/student/dashboard.html new file mode 100644 index 0000000..a2817a1 --- /dev/null +++ b/src/main/resources/templates/student/dashboard.html @@ -0,0 +1,80 @@ + + + + + Student Dashboard + + + +
+ +
+ + +
+
+
+
+

My Profile

+
+
+

Username:

+

Student ID:

+

Email:

+

Phone:

+

Address:

+
+
+
+ +
+
+
+

Edit Profile

+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ +
+
+

My Courses

+
+
+
+ You are not enrolled in any courses yet. +
+
    +
  • +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/student/profile.html b/src/main/resources/templates/student/profile.html deleted file mode 100644 index 95bd3c0..0000000 --- a/src/main/resources/templates/student/profile.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - Student Profile - - -

Student Profile

- -
- -

Your Information (Read-only):

-

Username:

-

Student ID/Roll: (Cannot be changed)

-

Department:

- -

Edit Profile Information:

-
-
- - -
-
- - -
-
- - -
- -
- -Back to Dashboard - - \ No newline at end of file diff --git a/src/main/resources/templates/teacher/add-course.html b/src/main/resources/templates/teacher/add-course.html index 73333f7..64fd00a 100644 --- a/src/main/resources/templates/teacher/add-course.html +++ b/src/main/resources/templates/teacher/add-course.html @@ -3,33 +3,39 @@ Add Course + -

Add New Course

+
-
-
+
+
+
+
+
+

Add New Course

+
+
+
-
-
- - + +
+ + +
+
+ + +
+ + Cancel + +
+
+
-
- - -
-
- - -
- - +
-Back to Courses List + \ No newline at end of file diff --git a/src/main/resources/templates/teacher/add-department.html b/src/main/resources/templates/teacher/add-department.html deleted file mode 100644 index 872be1f..0000000 --- a/src/main/resources/templates/teacher/add-department.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Add Department - - -

Add New Department

- -
-
- -
-
- - -
-
- - -
- -
- -Back to Departments List - - \ No newline at end of file diff --git a/src/main/resources/templates/teacher/add-student.html b/src/main/resources/templates/teacher/add-student.html index 41bb66a..c4e6e6d 100644 --- a/src/main/resources/templates/teacher/add-student.html +++ b/src/main/resources/templates/teacher/add-student.html @@ -3,49 +3,55 @@ Add Student + -

Add New Student

+
-
-
+
+
+
+
+
+

Add New Student

+
+
+
-
-
- - + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + Cancel + +
+
+
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- - +
-Back to Students List + \ No newline at end of file diff --git a/src/main/resources/templates/teacher/courses.html b/src/main/resources/templates/teacher/courses.html index 1b139a7..41ee172 100644 --- a/src/main/resources/templates/teacher/courses.html +++ b/src/main/resources/templates/teacher/courses.html @@ -3,44 +3,44 @@ Manage Courses + -

All Courses

+
-
+
+
+

Manage Courses

+ + Add New Course +
-Add New Course | -Enroll Students in Courses -

+
+ + +
- - - - - - - - - - - - - - - - - - - - - -
Course CodeCourse NameDepartmentTeacherNumber of StudentsActions
- Edit | - Delete | - View Students -
+
+ + + + + + + + + + + + + + + + + +
Course CodeCourse NameTeacherStudents Enrolled
+
+
-Back to Dashboard + \ No newline at end of file diff --git a/src/main/resources/templates/teacher/dashboard.html b/src/main/resources/templates/teacher/dashboard.html index a573910..9c581e4 100644 --- a/src/main/resources/templates/teacher/dashboard.html +++ b/src/main/resources/templates/teacher/dashboard.html @@ -3,27 +3,55 @@ Teacher Dashboard + -

Teacher Dashboard

-

Welcome, Teacher!

+
-

Management Actions:

- +
+
+
+

Teacher Dashboard

+
+
-

Student Actions:

- +
+
+
+
+
Total Students
+

0

+
+
+
+
+
+
+
Total Courses
+

0

+
+
+
+
-Back to Main Dashboard +
+
+ +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/teacher/departments.html b/src/main/resources/templates/teacher/departments.html deleted file mode 100644 index 8503ad8..0000000 --- a/src/main/resources/templates/teacher/departments.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Manage Departments - - -

All Departments

- -
- -Add New Department

- - - - - - - - - - - - - - - - -
Department CodeDepartment NameActions
- Edit | - Delete -
- -Back to Dashboard - - \ No newline at end of file diff --git a/src/main/resources/templates/teacher/edit-student.html b/src/main/resources/templates/teacher/edit-student.html index 2133849..f382631 100644 --- a/src/main/resources/templates/teacher/edit-student.html +++ b/src/main/resources/templates/teacher/edit-student.html @@ -3,41 +3,51 @@ Edit Student + -

Edit Student

+
-
+
+
+
+
+
+

Edit Student

+
+
+
-
-
- - + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + Cancel + +
+
+
-
- - -
-
- - -
-
- - -
-
- - -
- - Cancel - +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/teacher/enroll-student.html b/src/main/resources/templates/teacher/enroll-student.html index 795a3f0..e4ed977 100644 --- a/src/main/resources/templates/teacher/enroll-student.html +++ b/src/main/resources/templates/teacher/enroll-student.html @@ -2,69 +2,95 @@ - Enroll Student in Course + Enroll Student + -

Enroll Student in Course

+
-
-
+
+

Enroll Student in Course

-
-
- - +
+ +
-
- - +
+ +
+
+
+
+
New Enrollment
+
+
+ +
+ + +
+
+ + +
+ + +
+
+
+ +
+
+
+
Current Enrollments
+
+
+
+ + + + + + + + + + + + + +
StudentCourses
+ + + + + No courses enrolled + +
+
+
+
+
- - -
-

Current Enrollments

- - - - - - - - - - - - - - - - - -
StudentCourseDepartmentActions
-
    -
  • -
-
- Manage Courses -
+ +
-
-Back to Courses | -Back to Students | -Back to Dashboard + \ No newline at end of file diff --git a/src/main/resources/templates/teacher/manage-student-courses.html b/src/main/resources/templates/teacher/manage-student-courses.html deleted file mode 100644 index 33d5b80..0000000 --- a/src/main/resources/templates/teacher/manage-student-courses.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Manage Student Courses - - -

Manage Courses for

- -
-
- -

Current Enrolled Courses:

- -

No courses enrolled yet.

- -

Enroll in New Course:

-
-
- - -
- -
- -
-Back to Enrollment Page | -Back to Students | -Back to Dashboard - - \ No newline at end of file diff --git a/src/main/resources/templates/teacher/students.html b/src/main/resources/templates/teacher/students.html index c806f65..1b24996 100644 --- a/src/main/resources/templates/teacher/students.html +++ b/src/main/resources/templates/teacher/students.html @@ -2,61 +2,58 @@ - Students - + Manage Students + -

All Students

+
-
+
+
+

Manage Students

+ + Add New Student +
-Add New Student | -Enroll Students in Courses -

+
+ + +
- - - - - - - - - - - - - - - - - - - - - - - -
Student ID/RollUsernameEmailPhoneDepartmentCourses EnrolledActions
- - - - - None - - Edit | - Manage Courses | - Delete -
+
+ + + + + + + + + + + + + + + + + + + + + +
Student IDUsernameEmailPhoneCoursesActions
+ + + Edit + Delete +
+
+
-Back to Dashboard + \ No newline at end of file diff --git a/src/main/resources/templates/teacher/view-courses.html b/src/main/resources/templates/teacher/view-courses.html deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/StudentManagementAppApplicationTests.java b/src/test/java/com/shuvocse21/StudentManagementApp/StudentManagementAppApplicationTests.java index 9c13bae..674a9ac 100644 --- a/src/test/java/com/shuvocse21/StudentManagementApp/StudentManagementAppApplicationTests.java +++ b/src/test/java/com/shuvocse21/StudentManagementApp/StudentManagementAppApplicationTests.java @@ -1,13 +1,21 @@ package com.shuvocse21.StudentManagementApp; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest class StudentManagementAppApplicationTests { + @Autowired + private ApplicationContext context; + @Test void contextLoads() { + assertThat(context).isNotNull(); + assertThat(context.containsBean("userService")).isTrue(); + assertThat(context.containsBean("securityConfig")).isTrue(); } - -} +} \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/config/SecurityConfigTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/config/SecurityConfigTest.java new file mode 100644 index 0000000..4650a64 --- /dev/null +++ b/src/test/java/com/shuvocse21/StudentManagementApp/config/SecurityConfigTest.java @@ -0,0 +1,21 @@ +package com.shuvocse21.StudentManagementApp.config; + +import org.junit.jupiter.api.Test; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import static org.assertj.core.api.Assertions.assertThat; + +class SecurityConfigTest { + + @Test + void passwordEncoder() { + PasswordEncoder encoder = new BCryptPasswordEncoder(); + String rawPassword = "password123"; + String encoded = encoder.encode(rawPassword); + assertThat(encoded).isNotEqualTo(rawPassword); + assertThat(encoder.matches(rawPassword, encoded)).isTrue(); + } + + @Test + void filterChain() { } +} \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/controller/AuthControllerTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/controller/AuthControllerTest.java new file mode 100644 index 0000000..5236b63 --- /dev/null +++ b/src/test/java/com/shuvocse21/StudentManagementApp/controller/AuthControllerTest.java @@ -0,0 +1,75 @@ +package com.shuvocse21.StudentManagementApp.controller; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import com.shuvocse21.StudentManagementApp.service.UserService; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; + +@SpringBootTest +@AutoConfigureMockMvc +class AuthControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private UserService userService; + + @Test + void home() throws Exception { + mockMvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/login")); + } + + @Test + void login() throws Exception { + mockMvc.perform(get("/login")) + .andExpect(status().isOk()) + .andExpect(view().name("login")); + } + + @Test + @WithMockUser(roles = "STUDENT") + void dashboard_WithStudentRole() throws Exception { + mockMvc.perform(get("/dashboard")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/student/dashboard")); + } + + @Test + @WithMockUser(roles = "TEACHER") + void dashboard_WithTeacherRole() throws Exception { + mockMvc.perform(get("/dashboard")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/teacher/dashboard")); + } + + @Test + void registerTeacher_Get() throws Exception { + mockMvc.perform(get("/register/teacher")) + .andExpect(status().isOk()) + .andExpect(view().name("register-teacher")); + } + + @Test + void registerTeacher_Post() throws Exception { + mockMvc.perform(post("/register/teacher") + .with(csrf()) + .param("username", "johnteacher") + .param("password", "pass123") + .param("email", "john@school.com") + .param("employeeId", "T1001")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/login")) + .andExpect(flash().attributeExists("success")); + } +} \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/controller/StudentControllerTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/controller/StudentControllerTest.java index 0bf4b4a..fe986e0 100644 --- a/src/test/java/com/shuvocse21/StudentManagementApp/controller/StudentControllerTest.java +++ b/src/test/java/com/shuvocse21/StudentManagementApp/controller/StudentControllerTest.java @@ -1,16 +1,78 @@ package com.shuvocse21.StudentManagementApp.controller; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import com.shuvocse21.StudentManagementApp.service.UserService; +import com.shuvocse21.StudentManagementApp.dto.StudentDTO; +import com.shuvocse21.StudentManagementApp.entity.Student; +import com.shuvocse21.StudentManagementApp.entity.User; +import java.util.ArrayList; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.junit.jupiter.api.Assertions.*; - +@SpringBootTest +@AutoConfigureMockMvc class StudentControllerTest { + @Autowired + private MockMvc mockMvc; + + @MockBean + private UserService userService; + @Test - void profile() { + @WithMockUser(username = "student1", roles = "STUDENT") + void dashboard() throws Exception { + StudentDTO mockStudent = new StudentDTO(); + mockStudent.setUsername("student1"); + mockStudent.setStudentId("S1001"); + mockStudent.setEmail("student@test.com"); + mockStudent.setPhone("555-1234"); + mockStudent.setAddress("123 College Ave"); + mockStudent.setCourseCodes(new ArrayList<>()); + + User mockUser = new User(); + mockUser.setId(1L); + mockUser.setUsername("student1"); + + when(userService.getUserByUsername("student1")).thenReturn(mockUser); + when(userService.getStudentDTOByUserId(1L)).thenReturn(mockStudent); + + mockMvc.perform(get("/student/dashboard")) + .andExpect(status().isOk()) + .andExpect(view().name("student/dashboard")) + .andExpect(model().attributeExists("student")); } @Test - void updateProfile() { + @WithMockUser(username = "student1", roles = "STUDENT") + void updateProfile() throws Exception { + User mockUser = new User(); + mockUser.setId(1L); + mockUser.setUsername("student1"); + + Student mockStudent = new Student(); + mockStudent.setId(1L); + mockStudent.setUser(mockUser); + + when(userService.getUserByUsername("student1")).thenReturn(mockUser); + when(userService.getStudentByUserId(1L)).thenReturn(mockStudent); + + mockMvc.perform(post("/student/profile/update") + .with(csrf()) + .param("email", "updated@test.com") + .param("phone", "555-9999") + .param("address", "456 New St")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/student/dashboard")) + .andExpect(flash().attributeExists("success")); } } \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/controller/TeacherControllerTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/controller/TeacherControllerTest.java new file mode 100644 index 0000000..86d66b0 --- /dev/null +++ b/src/test/java/com/shuvocse21/StudentManagementApp/controller/TeacherControllerTest.java @@ -0,0 +1,107 @@ +package com.shuvocse21.StudentManagementApp.controller; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import com.shuvocse21.StudentManagementApp.service.UserService; +import com.shuvocse21.StudentManagementApp.repository.CourseRepository; +import com.shuvocse21.StudentManagementApp.repository.StudentRepository; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; + +@SpringBootTest +@AutoConfigureMockMvc +class TeacherControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private UserService userService; + @MockBean + private CourseRepository courseRepository; + @MockBean + private StudentRepository studentRepository; + + // ESSENTIAL METHODS (Filled) + @Test + @WithMockUser(roles = "TEACHER") + void dashboard() throws Exception { + mockMvc.perform(get("/teacher/dashboard")) + .andExpect(status().isOk()) + .andExpect(view().name("teacher/dashboard")); + } + + @Test + @WithMockUser(roles = "TEACHER") + void viewStudents() throws Exception { + mockMvc.perform(get("/teacher/students")) + .andExpect(status().isOk()) + .andExpect(view().name("teacher/students")); + } + + @Test + @WithMockUser(roles = "TEACHER") + void addStudent() throws Exception { + mockMvc.perform(post("/teacher/add-student") + .with(csrf()) + .param("username", "newstudent") + .param("password", "pass123") + .param("email", "student@test.com") + .param("studentId", "S1001") + .param("phone", "555-1234") + .param("address", "123 College Ave")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/teacher/students")) + .andExpect(flash().attributeExists("success")); + } + + @Test + @WithMockUser(roles = "TEACHER") + void deleteStudent() throws Exception { + mockMvc.perform(get("/teacher/delete-student/1")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/teacher/students")) + .andExpect(flash().attributeExists("success")); + } + + @Test + @WithMockUser(roles = "TEACHER") + void enrollStudent() throws Exception { + mockMvc.perform(post("/teacher/enroll-student") + .with(csrf()) + .param("studentId", "1") + .param("courseId", "1")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/teacher/enroll-student")) + .andExpect(flash().attributeExists("success")); + } + + // NON-ESSENTIAL METHODS (Blank) + @Test + void showAddStudentForm() { } + + @Test + void editStudentForm() { } + @Test + + void updateStudent() { } + + @Test + void viewCourses() { } + + @Test + void showAddCourseForm() { } + + @Test + void addCourse() { } + + @Test + void showEnrollStudentForm() { } +} \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/repository/CourseRepositoryTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/repository/CourseRepositoryTest.java new file mode 100644 index 0000000..7102750 --- /dev/null +++ b/src/test/java/com/shuvocse21/StudentManagementApp/repository/CourseRepositoryTest.java @@ -0,0 +1,58 @@ +package com.shuvocse21.StudentManagementApp.repository; + +import com.shuvocse21.StudentManagementApp.entity.Course; +import com.shuvocse21.StudentManagementApp.entity.Teacher; +import com.shuvocse21.StudentManagementApp.entity.User; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +class CourseRepositoryTest { + + @Autowired + private CourseRepository courseRepository; + @Autowired + private TeacherRepository teacherRepository; + @Autowired + private UserRepository userRepository; + + // ESSENTIAL METHOD + @Test + void findByTeacherId() { + User user = new User(); + user.setUsername("professorsmith"); + user.setPassword("pass123"); + user.setEmail("smith@school.com"); + user.setRole("TEACHER"); + user.setEnabled(true); + User savedUser = userRepository.save(user); + + Teacher teacher = new Teacher(); + teacher.setUser(savedUser); + teacher.setEmployeeId("T2001"); + Teacher savedTeacher = teacherRepository.save(teacher); + + Course course1 = new Course(); + course1.setName("Mathematics"); + course1.setCode("MATH201"); + course1.setTeacher(savedTeacher); + courseRepository.save(course1); + + Course course2 = new Course(); + course2.setName("Physics"); + course2.setCode("PHY201"); + course2.setTeacher(savedTeacher); + courseRepository.save(course2); + + List foundCourses = courseRepository.findByTeacherId(savedTeacher.getId()); + + assertThat(foundCourses).hasSize(2); + assertThat(foundCourses).extracting(Course::getCode) + .containsExactlyInAnyOrder("MATH201", "PHY201"); + } + + // NON-ESSENTIAL METHODS (None needed for this file) +} \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/repository/StudentRepositoryTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/repository/StudentRepositoryTest.java new file mode 100644 index 0000000..bc7f564 --- /dev/null +++ b/src/test/java/com/shuvocse21/StudentManagementApp/repository/StudentRepositoryTest.java @@ -0,0 +1,49 @@ +package com.shuvocse21.StudentManagementApp.repository; + +import com.shuvocse21.StudentManagementApp.entity.Student; +import com.shuvocse21.StudentManagementApp.entity.User; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import java.util.Optional; +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +class StudentRepositoryTest { + + @Autowired + private StudentRepository studentRepository; + @Autowired + private UserRepository userRepository; + + // ESSENTIAL METHOD + @Test + void findByStudentId() { + User user = new User(); + user.setUsername("studentjane"); + user.setPassword("pass123"); + user.setEmail("jane@student.com"); + user.setRole("STUDENT"); + user.setEnabled(true); + User savedUser = userRepository.save(user); + + Student student = new Student(); + student.setUser(savedUser); + student.setStudentId("S2001"); + student.setPhone("555-0101"); + student.setAddress("123 Campus Drive"); + studentRepository.save(student); + + Optional found = studentRepository.findByStudentId("S2001"); + + assertThat(found).isPresent(); + assertThat(found.get().getStudentId()).isEqualTo("S2001"); + } + + // NON-ESSENTIAL METHODS (Blank) + @Test + void findByUserId() { } + + @Test + void existsByStudentId() { } +} \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/repository/TeacherRepositoryTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/repository/TeacherRepositoryTest.java new file mode 100644 index 0000000..4be82e7 --- /dev/null +++ b/src/test/java/com/shuvocse21/StudentManagementApp/repository/TeacherRepositoryTest.java @@ -0,0 +1,45 @@ +package com.shuvocse21.StudentManagementApp.repository; + +import com.shuvocse21.StudentManagementApp.entity.Teacher; +import com.shuvocse21.StudentManagementApp.entity.User; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +class TeacherRepositoryTest { + + @Autowired + private TeacherRepository teacherRepository; + @Autowired + private UserRepository userRepository; + + // ESSENTIAL METHOD + @Test + void findByUserId() { + User user = new User(); + user.setUsername("drjones"); + user.setPassword("pass"); + user.setEmail("jones@school.com"); + user.setRole("TEACHER"); + User savedUser = userRepository.save(user); + + Teacher teacher = new Teacher(); + teacher.setUser(savedUser); + teacher.setEmployeeId("T1002"); + teacherRepository.save(teacher); + + Teacher found = teacherRepository.findByUserId(savedUser.getId()).orElse(null); + + assertThat(found).isNotNull(); + assertThat(found.getEmployeeId()).isEqualTo("T1002"); + } + + // NON-ESSENTIAL METHODS (Blank) + @Test + void save() { } + + @Test + void existsByEmployeeId() { } +} \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/repository/UserRepositoryTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/repository/UserRepositoryTest.java new file mode 100644 index 0000000..9f223d2 --- /dev/null +++ b/src/test/java/com/shuvocse21/StudentManagementApp/repository/UserRepositoryTest.java @@ -0,0 +1,40 @@ +package com.shuvocse21.StudentManagementApp.repository; + +import com.shuvocse21.StudentManagementApp.entity.User; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +class UserRepositoryTest { + + @Autowired + private UserRepository userRepository; + + // ESSENTIAL METHOD + @Test + void findByUsername() { + User user = new User(); + user.setUsername("jane"); + user.setPassword("pass"); + user.setEmail("jane@test.com"); + user.setRole("STUDENT"); + userRepository.save(user); + + User found = userRepository.findByUsername("jane").orElse(null); + + assertThat(found).isNotNull(); + assertThat(found.getEmail()).isEqualTo("jane@test.com"); + } + + // NON-ESSENTIAL METHODS (Blank) + @Test + void save() { } + + @Test + void existsByUsername() { } + + @Test + void existsByEmail() { } +} \ No newline at end of file diff --git a/src/test/java/com/shuvocse21/StudentManagementApp/service/UserServiceTest.java b/src/test/java/com/shuvocse21/StudentManagementApp/service/UserServiceTest.java new file mode 100644 index 0000000..1900f8c --- /dev/null +++ b/src/test/java/com/shuvocse21/StudentManagementApp/service/UserServiceTest.java @@ -0,0 +1,134 @@ +package com.shuvocse21.StudentManagementApp.service; + +import com.shuvocse21.StudentManagementApp.entity.*; +import com.shuvocse21.StudentManagementApp.repository.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.crypto.password.PasswordEncoder; +import java.util.Optional; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class UserServiceTest { + + @Mock private UserRepository userRepository; + @Mock private StudentRepository studentRepository; + @Mock private TeacherRepository teacherRepository; + @Mock private CourseRepository courseRepository; + @Mock private PasswordEncoder passwordEncoder; + @InjectMocks private UserService userService; + + private User testUser; + private Student testStudent; + + @BeforeEach + void setUp() { + testUser = new User(); + testUser.setId(1L); + testUser.setUsername("testuser"); + testUser.setEmail("test@test.com"); + testUser.setRole("STUDENT"); + + testStudent = new Student(); + testStudent.setId(1L); + testStudent.setUser(testUser); + testStudent.setStudentId("S1001"); + } + + // ESSENTIAL METHODS + @Test + void registerStudent() { + when(userRepository.existsByUsername("newstudent")).thenReturn(false); + when(userRepository.existsByEmail("new@test.com")).thenReturn(false); + when(studentRepository.existsByStudentId("S1002")).thenReturn(false); + when(passwordEncoder.encode("pass")).thenReturn("encoded"); + when(userRepository.save(any(User.class))).thenReturn(testUser); + when(studentRepository.save(any(Student.class))).thenReturn(testStudent); + + User result = userService.registerStudent("newstudent", "pass", "new@test.com", + "S1002", "555-1234", "123 St"); + + assertThat(result).isNotNull(); + verify(userRepository).save(any(User.class)); + verify(studentRepository).save(any(Student.class)); + } + + @Test + void registerStudentDuplicateUsername() { + when(userRepository.existsByUsername("existing")).thenReturn(true); + + assertThatThrownBy(() -> + userService.registerStudent("existing", "pass", "email", "SID", "phone", "addr")) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Username already exists"); + } + + @Test + void registerTeacher() { + when(userRepository.existsByUsername("newteacher")).thenReturn(false); + when(userRepository.existsByEmail("teacher@test.com")).thenReturn(false); + when(teacherRepository.existsByEmployeeId("T1002")).thenReturn(false); + when(passwordEncoder.encode("pass")).thenReturn("encoded"); + when(userRepository.save(any(User.class))).thenReturn(testUser); + + User result = userService.registerTeacher("newteacher", "pass", "teacher@test.com", "T1002"); + + assertThat(result).isNotNull(); + verify(userRepository).save(any(User.class)); + } + + @Test + void enrollStudentInCourse() { + Course testCourse = new Course(); + testCourse.setId(1L); + + when(studentRepository.findById(1L)).thenReturn(Optional.of(testStudent)); + when(courseRepository.findById(1L)).thenReturn(Optional.of(testCourse)); + + userService.enrollStudentInCourse(1L, 1L); + + verify(studentRepository).save(any(Student.class)); + assertThat(testStudent.getCourses()).contains(testCourse); + } + + // NON-ESSENTIAL METHODS (Blank) + @Test + void getUserByUsername() { } + + @Test + void getStudentByUserId() { } + + @Test + void getStudentDTOByUserId() { } + + @Test + void getAllStudentDTOs() { } + + @Test + void getAllCourseDTOs() { } + + @Test + void updateStudent() { } + + @Test + void deleteStudent() { } + + @Test + void removeStudentFromCourse() { } + + @Test + void getStudentById() { } + + @Test + void getCourseById() { } + + @Test + void getAllCourses() { } +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..fdbfd3c --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,8 @@ +# PostgreSQL for CI/CD +spring.datasource.url=jdbc:postgresql://localhost:5432/smsdb_test +spring.datasource.username=admin +spring.datasource.password=pass123 +spring.datasource.driver-class-name=org.postgresql.Driver +spring.jpa.hibernate.ddl-auto=create-drop +server.port=0 +spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration \ No newline at end of file