diff --git a/pom.xml b/pom.xml index 7e35f61..dee52ab 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,6 @@ org.springframework.boot spring-boot-starter-web - org.springframework.boot spring-boot-devtools @@ -47,6 +46,18 @@ spring-security-crypto 3.1.0.RELEASE + + + org.seleniumhq.selenium + selenium-java + + + org.seleniumhq.selenium + selenium-api + test + + + diff --git a/src/main/java/com/cst438/Cst438Assignment2Main.java b/src/main/java/com/cst438/Cst438Assignment2Main.java index a0bcaf6..e811fbd 100644 --- a/src/main/java/com/cst438/Cst438Assignment2Main.java +++ b/src/main/java/com/cst438/Cst438Assignment2Main.java @@ -10,4 +10,6 @@ public static void main(String[] args) { SpringApplication.run(Cst438Assignment2Main.class, args); } + + } diff --git a/src/main/java/com/cst438/controller/AssignmentController.java b/src/main/java/com/cst438/controller/AssignmentController.java index 4d0ad35..d437289 100644 --- a/src/main/java/com/cst438/controller/AssignmentController.java +++ b/src/main/java/com/cst438/controller/AssignmentController.java @@ -16,11 +16,22 @@ import java.util.ArrayList; import java.util.List; +import java.sql.Date; @RestController @CrossOrigin(origins = "http://localhost:3000") public class AssignmentController { + @Autowired + AssignmentRepository assignmentRepository; + @Autowired + CourseRepository courseRepository; + @Autowired + SectionRepository sectionRepository; + @Autowired + EnrollmentRepository enrollmentRepository; + @Autowired + GradeRepository gradeRepository; // instructor lists assignments for a section. Assignments ordered by due date. // logged in user must be the instructor for the section @@ -28,25 +39,70 @@ public class AssignmentController { public List getAssignments( @PathVariable("secNo") int secNo) { - // TODO remove the following line when done - - // hint: use the assignment repository method - // findBySectionNoOrderByDueDate to return - // a list of assignments - - return null; + List assignments = assignmentRepository.findBySectionNoOrderByDueDate(secNo); + if (assignments.isEmpty()) { + throw new ResponseStatusException( HttpStatus.NOT_FOUND, "section not found " + secNo); + } + List dto_list = new ArrayList<>(); + for(Assignment a:assignments){ + dto_list.add(new AssignmentDTO( + a.getAssignmentId(), + a.getTitle(), + a.getDueDate().toString(), + a.getSection().getCourse().getCourseId(), + a.getSection().getSecId(), + a.getSection().getSectionNo() + )); + } + + // hint: use the assignment repository method + // findBySectionNoOrderByDueDate to return + // a list of assignments + + return dto_list; } // add assignment // user must be instructor of the section // return AssignmentDTO with assignmentID generated by database @PostMapping("/assignments") - public AssignmentDTO createAssignment( - @RequestBody AssignmentDTO dto) { + public AssignmentDTO createAssignment(@RequestBody AssignmentDTO dto) { - // TODO remove the following line when done + Section s = sectionRepository.findSectionBySectionNo(dto.secNo()); + if (s==null) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "section not found " + dto.secNo()); + } + if(s.getSectionNo()!=dto.secNo()){ + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "sectionNo not matched " + dto.secNo()); + } - return null; + // TODO remove the following line when done + Assignment a = new Assignment(); + a.setAssignmentId(dto.id()); + a.setTitle(dto.title()); + //convert string -> date + Date date = Date.valueOf(dto.dueDate()); + + Date startDate = s.getTerm().getStartDate(); + Date dropDate = s.getTerm().getEndDate(); + if(date.before(startDate)){ + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "DueDate is before course StartDate "); + }if(date.after(dropDate)){ + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "DueDate is after course EndDate "); + } + + a.setDueDate(date); + a.setSection(s); + assignmentRepository.save(a); + + return new AssignmentDTO( + a.getAssignmentId(), + a.getTitle(), + a.getDueDate().toString(), + a.getSection().getCourse().getCourseId(), + a.getSection().getSecId(), + a.getSection().getSectionNo() + ); } // update assignment for a section. Only title and dueDate may be changed. @@ -55,9 +111,33 @@ public AssignmentDTO createAssignment( @PutMapping("/assignments") public AssignmentDTO updateAssignment(@RequestBody AssignmentDTO dto) { - // TODO remove the following line when done - - return null; + Assignment a = assignmentRepository.findById(dto.id()).orElse(null); + if(a==null){ + throw new ResponseStatusException( HttpStatus.NOT_FOUND, "Assignment not found "+dto.id()); + } + + a.setTitle(dto.title()); + //convert string -> date + Date date = Date.valueOf(dto.dueDate()); + + Date startDate = a.getSection().getTerm().getStartDate(); + Date dropDate = a.getSection().getTerm().getEndDate(); + if(date.before(startDate)){ + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "DueDate is before course StartDate "); + }if(date.after(dropDate)){ + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "DueDate is after course EndDate "); + } + + a.setDueDate(date); + assignmentRepository.save(a); + return new AssignmentDTO( + a.getAssignmentId(), + a.getTitle(), + a.getDueDate().toString(), + a.getSection().getCourse().getCourseId(), + a.getSection().getSecId(), + a.getSection().getSectionNo() + ); } // delete assignment for a section @@ -65,7 +145,11 @@ public AssignmentDTO updateAssignment(@RequestBody AssignmentDTO dto) { @DeleteMapping("/assignments/{assignmentId}") public void deleteAssignment(@PathVariable("assignmentId") int assignmentId) { - // TODO + Assignment a = assignmentRepository.findById(assignmentId).orElse(null); + //if assignment doesn't exist, do nothing + if(a!=null){ + assignmentRepository.delete(a); + } } // instructor gets grades for assignment ordered by student name @@ -74,15 +158,46 @@ public void deleteAssignment(@PathVariable("assignmentId") int assignmentId) { public List getAssignmentGrades(@PathVariable("assignmentId") int assignmentId) { // TODO remove the following line when done + Assignment a = assignmentRepository.findById(assignmentId).orElse(null); + if (a==null) { + throw new ResponseStatusException( HttpStatus.NOT_FOUND, "assignment not found "+assignmentId); + } // get the list of enrollments for the section related to this assignment. - // hint: use te enrollment repository method findEnrollmentsBySectionOrderByStudentName. - // for each enrollment, get the grade related to the assignment and enrollment - // hint: use the gradeRepository findByEnrollmentIdAndAssignmentId method. - // if the grade does not exist, create a grade entity and set the score to NULL - // and then save the new entity + // hint: use the enrollment repository method findEnrollmentsBySectionOrderByStudentName. + List enrollments = enrollmentRepository.findEnrollmentsBySectionNoOrderByStudentName(a.getSection().getSectionNo()); + if (enrollments.isEmpty()) { + throw new ResponseStatusException( HttpStatus.NOT_FOUND, "Enrollment not found"); + } - return null; + // for each enrollment, get the grade related to the assignment and enrollment + // hint: use the gradeRepository findByEnrollmentIdAndAssignmentId method. + List gradeDTOList = new ArrayList<>(); + if(enrollments.isEmpty()){ + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Enrollment not found"); + } + for(Enrollment e:enrollments){ + // if the grade does not exist, create a grade entity and set the score to NULL + Grade g = gradeRepository.findByEnrollmentIdAndAssignmentId(e.getEnrollmentId(), assignmentId); + if(g==null){ + g = new Grade(); + g.setAssignment(a); + g.setEnrollment(e); + g.setScore(null); + // and then save the new entity + gradeRepository.save(g); + } + gradeDTOList.add(new GradeDTO( + g.getGradeId(), + e.getStudent().getName(), + e.getStudent().getEmail(), + a.getTitle(), + e.getSection().getCourse().getCourseId(), + e.getSection().getSecId(), + g.getScore() + )); + } + return gradeDTOList; } // instructor uploads grades for assignment @@ -90,15 +205,18 @@ public List getAssignmentGrades(@PathVariable("assignmentId") int assi @PutMapping("/grades") public void updateGrades(@RequestBody List dlist) { - // TODO - // for each grade in the GradeDTO list, retrieve the grade entity - // update the score and save the entity - + for(GradeDTO dto:dlist){ + Grade g = gradeRepository.findById(dto.gradeId()).orElse(null); + if(g==null){ + throw new ResponseStatusException( HttpStatus.NOT_FOUND, "Grade not found "+dto.gradeId()); + } + // update the score and save the entity + g.setScore(dto.score()); + gradeRepository.save(g); + } } - - // student lists their assignments/grades for an enrollment ordered by due date // student must be enrolled in the section @GetMapping("/assignments") @@ -108,11 +226,29 @@ public List getStudentAssignments( @RequestParam("semester") String semester) { // TODO remove the following line when done + List returnDtos = new ArrayList<>(); + List enrollments = enrollmentRepository.findByYearAndSemesterOrderByCourseId(year, semester, studentId); + List assignments = new ArrayList<>(); + for (Enrollment e:enrollments){ + assignments = assignmentRepository.findBySectionNoOrderByDueDate(e.getSection().getSectionNo()); + for(Assignment a:assignments){ + Grade grade = gradeRepository.findByEnrollmentIdAndAssignmentId(e.getEnrollmentId(), a.getAssignmentId()); + returnDtos.add(new AssignmentStudentDTO( + a.getAssignmentId(), + a.getTitle(), + a.getDueDate(), + e.getSection().getCourse().getCourseId(), + e.getSection().getSecId(), + (grade!=null) ? grade.getScore() : null + )); + } + } + // return a list of assignments and (if they exist) the assignment grade // for all sections that the student is enrolled for the given year and semester - // hint: use the assignment repository method findByStudentIdAndYearAndSemesterOrderByDueDate + // hint: use the assignment repository method findByStudentIdAndYearAndSemesterOrderByDueDate - return null; + return returnDtos; } -} +} \ No newline at end of file diff --git a/src/main/java/com/cst438/controller/EnrollmentController.java b/src/main/java/com/cst438/controller/EnrollmentController.java index bcccc61..c88efd8 100644 --- a/src/main/java/com/cst438/controller/EnrollmentController.java +++ b/src/main/java/com/cst438/controller/EnrollmentController.java @@ -1,42 +1,62 @@ package com.cst438.controller; - +import com.cst438.domain.Enrollment; +import com.cst438.domain.EnrollmentRepository; import com.cst438.dto.EnrollmentDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @RestController @CrossOrigin(origins = "http://localhost:3000") public class EnrollmentController { + @Autowired + private EnrollmentRepository enrollmentRepository; + // instructor downloads student enrollments for a section, ordered by student name // user must be instructor for the section @GetMapping("/sections/{sectionNo}/enrollments") - public List getEnrollments( - @PathVariable("sectionNo") int sectionNo ) { - - // TODO - // hint: use enrollment repository findEnrollmentsBySectionNoOrderByStudentName method - // remove the following line when done - return null; + public List getEnrollments(@PathVariable("sectionNo") int sectionNo) { + List enrollments = enrollmentRepository.findEnrollmentsBySectionNoOrderByStudentName(sectionNo); + if(enrollments == null) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Section number is invalid"); + } + + return enrollments.stream() + .map(enrollment -> new EnrollmentDTO( + enrollment.getEnrollmentId(), + enrollment.getGrade(), + enrollment.getStudent().getId(), + enrollment.getStudent().getName(), + enrollment.getStudent().getEmail(), + enrollment.getSection().getCourse().getCourseId(), + enrollment.getSection().getSecId(), + enrollment.getSection().getSectionNo(), + enrollment.getSection().getBuilding(), + enrollment.getSection().getRoom(), + enrollment.getSection().getTimes(), + enrollment.getSection().getCourse().getCredits(), + enrollment.getSection().getTerm().getYear(), + enrollment.getSection().getTerm().getSemester())) + .collect(Collectors.toList()); } // instructor uploads enrollments with the final grades for the section // user must be instructor for the section @PutMapping("/enrollments") - public void updateEnrollmentGrade(@RequestBody List dlist) { - - // TODO - - // For each EnrollmentDTO in the list - // find the Enrollment entity using enrollmentId - // update the grade and save back to database - + public void updateEnrollmentGrade(@RequestBody List dtoList) { + for (EnrollmentDTO dto : dtoList) { + Enrollment enrollment = enrollmentRepository.findById(dto.enrollmentId()) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Enrollment not found")); + + // Update the grade and save back to the database + enrollment.setGrade(dto.grade()); + enrollmentRepository.save(enrollment); + } } - -} +} \ No newline at end of file diff --git a/src/main/java/com/cst438/controller/SectionController.java b/src/main/java/com/cst438/controller/SectionController.java index 746c624..2a0540c 100644 --- a/src/main/java/com/cst438/controller/SectionController.java +++ b/src/main/java/com/cst438/controller/SectionController.java @@ -6,6 +6,8 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; @@ -14,6 +16,7 @@ @CrossOrigin(origins = "http://localhost:3000") public class SectionController { + private static final Logger logger = LoggerFactory.getLogger(SectionController.class.getName()); @Autowired CourseRepository courseRepository; @@ -152,12 +155,15 @@ public List getSections( @GetMapping("/sections") public List getSectionsForInstructor( @RequestParam("email") String instructorEmail, - @RequestParam("year") int year , - @RequestParam("semester") String semester ) { + @RequestParam("year") int year, + @RequestParam("semester") String semester) { + logger.info("Fetching sections for instructor: {}, year: {}, semester: {}", instructorEmail, year, semester); List
sections = sectionRepository.findByInstructorEmailAndYearAndSemester(instructorEmail, year, semester); + logger.info("Found {} sections for instructor: {}, year: {}, semester: {}", sections.size(), instructorEmail, year, semester); + List dto_list = new ArrayList<>(); for (Section s : sections) { User instructor = null; @@ -179,7 +185,7 @@ public List getSectionsForInstructor( } return dto_list; } - + @GetMapping("/sections/open") public List getOpenSectionsForEnrollment() { @@ -203,4 +209,4 @@ public List getOpenSectionsForEnrollment() { } return dlist; } -} +} \ No newline at end of file diff --git a/src/main/java/com/cst438/controller/StudentController.java b/src/main/java/com/cst438/controller/StudentController.java index e34f59f..46c1942 100644 --- a/src/main/java/com/cst438/controller/StudentController.java +++ b/src/main/java/com/cst438/controller/StudentController.java @@ -1,7 +1,9 @@ package com.cst438.controller; import com.cst438.domain.*; +import com.cst438.dto.CourseDTO; import com.cst438.dto.EnrollmentDTO; +import com.sun.tools.jconsole.JConsoleContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @@ -9,6 +11,7 @@ import java.util.ArrayList; import java.util.Date; +import java.text.SimpleDateFormat; import java.util.List; import java.util.Optional; @@ -16,6 +19,21 @@ @CrossOrigin(origins = "http://localhost:3000") public class StudentController { + @Autowired + CourseRepository courseRepository; + + @Autowired + SectionRepository sectionRepository; + + @Autowired + TermRepository termRepository; + + @Autowired + UserRepository userRepository; + + @Autowired + EnrollmentRepository enrollmentRepository; + // student gets transcript showing list of all enrollments // studentId will be temporary until Login security is implemented @@ -23,13 +41,43 @@ public class StudentController { @GetMapping("/transcripts") public List getTranscript(@RequestParam("studentId") int studentId) { - // TODO - // list course_id, sec_id, title, credit, grade in chronological order // user must be a student // hint: use enrollment repository method findEnrollmentByStudentIdOrderByTermId - // remove the following line when done - return null; + + List enrollments = enrollmentRepository.findEnrollmentsByStudentIdOrderByTermId(studentId); + List dto_list = new ArrayList<>(); + boolean studentFound = false; + for (Enrollment e : enrollments) { + if (e.getStudent().getId() == studentId) { + studentFound = true; + break; + } + } + if (!studentFound) { + throw new ResponseStatusException( HttpStatus.NOT_FOUND, "Student not found "+ studentId); + } else { + for (Enrollment e : enrollments) { + dto_list.add(new EnrollmentDTO( + e.getEnrollmentId(), + e.getGrade(), + studentId, + e.getStudent().getName(), + e.getStudent().getEmail(), + e.getSection().getCourse().getCourseId(), + e.getSection().getSecId(), + e.getSection().getSectionNo(), + e.getSection().getBuilding(), + e.getSection().getRoom(), + e.getSection().getTimes(), + e.getSection().getCourse().getCredits(), + e.getSection().getTerm().getYear(), + e.getSection().getTerm().getSemester())); + } + + return dto_list; + + } } // student gets a list of their enrollments for the given year, semester @@ -41,14 +89,36 @@ public List getSchedule( @RequestParam("semester") String semester, @RequestParam("studentId") int studentId) { - // TODO // hint: use enrollment repository method findByYearAndSemesterOrderByCourseId - // remove the following line when done - return null; + List enrollments = enrollmentRepository.findByYearAndSemesterOrderByCourseId(year, semester, studentId); + if (enrollments.isEmpty()) { + throw new ResponseStatusException( HttpStatus.NOT_FOUND, "Enrollment not found"); + } + List dto_list = new ArrayList<>(); + for (Enrollment e : enrollments) { + dto_list.add(new EnrollmentDTO( + e.getEnrollmentId(), + e.getGrade(), + studentId, + e.getStudent().getName(), + e.getStudent().getEmail(), + e.getSection().getCourse().getCourseId(), + e.getSection().getSecId(), + e.getSection().getSectionNo(), + e.getSection().getBuilding(), + e.getSection().getRoom(), + e.getSection().getTimes(), + e.getSection().getCourse().getCredits(), + e.getSection().getTerm().getYear(), + e.getSection().getTerm().getSemester())); + } + + return dto_list; + + // return null; } - // student adds enrollment into a section // user must be student // return EnrollmentDTO with enrollmentId generated by database @@ -58,24 +128,87 @@ public EnrollmentDTO addCourse( @RequestParam("studentId") int studentId ) { // TODO - // check that the Section entity with primary key sectionNo exists + Section s = sectionRepository.findById(sectionNo).orElse(null); + if (s==null){ + throw new ResponseStatusException( HttpStatus.NOT_FOUND, "section not found " + sectionNo); + } + // check that today is between addDate and addDeadline for the section + long millis = System.currentTimeMillis(); + java.sql.Date today = new java.sql.Date(millis); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd"); + Date addDate = s.getTerm().getAddDate(); + Date addDeadline = s.getTerm().getAddDeadline(); + if (today.before(addDate) || today.after(addDeadline)) { + throw new ResponseStatusException( HttpStatus.BAD_REQUEST, "Invalid date"); + } + // check that student is not already enrolled into this section + Enrollment e = enrollmentRepository.findEnrollmentBySectionNoAndStudentId(sectionNo, studentId); + Section section = sectionRepository.findSectionBySectionNo(sectionNo); + List users = userRepository.findAllByOrderByIdAsc(); + User student = new User(); + for (User u : users) { + if (u.getId() == studentId) { + student = u; + break; + } + } + // create a new enrollment entity and save. The enrollment grade will // be NULL until instructor enters final grades for the course. - - // remove the following line when done. - return null; + if (e != null) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "already enrolled in this section"); + } else { + e = new Enrollment(); + e.setSection(section); + e.setUser(student); + enrollmentRepository.save(e); + return new EnrollmentDTO( + e.getEnrollmentId(), + "", + studentId, + e.getStudent().getName(), + e.getStudent().getEmail(), + e.getSection().getCourse().getCourseId(), + e.getSection().getSecId(), + e.getSection().getSectionNo(), + e.getSection().getBuilding(), + e.getSection().getRoom(), + e.getSection().getTimes(), + e.getSection().getCourse().getCredits(), + e.getSection().getTerm().getYear(), + e.getSection().getTerm().getSemester() + ); + } } // student drops a course - // user must be student @DeleteMapping("/enrollments/{enrollmentId}") public void dropCourse(@PathVariable("enrollmentId") int enrollmentId) { - // TODO + Enrollment e = enrollmentRepository.findEnrollmentByEnrollmentId(enrollmentId); + if (e == null) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Enrollment does not exist"); + } + + // user must be student + if (!e.getStudent().getType().equals("STUDENT")) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "User must be student"); + } + // check that today is not after the dropDeadline for section + long millis = System.currentTimeMillis(); + java.sql.Date today = new java.sql.Date(millis); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd"); + Date dropDate = e.getSection().getTerm().getDropDeadline(); + if (today.after(dropDate)) { + throw new ResponseStatusException( HttpStatus.BAD_REQUEST, "Too late to drop. Sorry, the money is ours"); + } else { + enrollmentRepository.delete(e); + } + } -} \ No newline at end of file +} diff --git a/src/main/java/com/cst438/domain/Assignment.java b/src/main/java/com/cst438/domain/Assignment.java index c669834..d4a4372 100644 --- a/src/main/java/com/cst438/domain/Assignment.java +++ b/src/main/java/com/cst438/domain/Assignment.java @@ -4,16 +4,56 @@ import java.sql.Date; import java.text.SimpleDateFormat; - +//Change! @Entity public class Assignment { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="assignment_id") private int assignmentId; - - // TODO complete this class + // add additional attributes for title, dueDate + private String title; + + @Column(name = "due_date") + private Date dueDate; // add relationship between assignment and section entities + @ManyToOne + @JoinColumn(name = "section_no", nullable = false) + private Section section; // add getter and setter methods + + public int getAssignmentId() { + return assignmentId; + } + + public void setAssignmentId(int assignmentId) { + this.assignmentId = assignmentId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Date getDueDate() { + return dueDate; + } + + public void setDueDate(Date dueDate) { + this.dueDate = dueDate; + } + + public Section getSection() { + return section; + } + + public void setSection(Section section) { + this.section = section; + } + + } diff --git a/src/main/java/com/cst438/domain/AssignmentRepository.java b/src/main/java/com/cst438/domain/AssignmentRepository.java index 2f04eaa..4d533a7 100644 --- a/src/main/java/com/cst438/domain/AssignmentRepository.java +++ b/src/main/java/com/cst438/domain/AssignmentRepository.java @@ -8,13 +8,14 @@ public interface AssignmentRepository extends CrudRepository { // TODO uncomment the following lines as needed + // This is my comment -// @Query("select a from Assignment a where a.section.sectionNo=:sectionNo order by a.dueDate") -// List findBySectionNoOrderByDueDate(int sectionNo); -// -// @Query("select a from Assignment a join a.section.enrollments e " + -// "where a.section.term.year=:year and a.section.term.semester=:semester and" + -// " e.student.id=:studentId order by a.dueDate") -// List findByStudentIdAndYearAndSemesterOrderByDueDate(int studentId, int year, String semester); + @Query("select a from Assignment a where a.section.sectionNo=:sectionNo order by a.dueDate") + List findBySectionNoOrderByDueDate(int sectionNo); + + @Query("select a from Assignment a join a.section.enrollments e " + + "where a.section.term.year=:year and a.section.term.semester=:semester and" + + " e.student.id=:studentId order by a.dueDate") + List findByStudentIdAndYearAndSemesterOrderByDueDate(int studentId, int year, String semester); } diff --git a/src/main/java/com/cst438/domain/CourseRepository.java b/src/main/java/com/cst438/domain/CourseRepository.java index 3cc6216..328c742 100644 --- a/src/main/java/com/cst438/domain/CourseRepository.java +++ b/src/main/java/com/cst438/domain/CourseRepository.java @@ -4,6 +4,6 @@ import java.util.List; public interface CourseRepository extends CrudRepository { - + List findAllByOrderByCourseIdAsc(); -} +} \ No newline at end of file diff --git a/src/main/java/com/cst438/domain/Enrollment.java b/src/main/java/com/cst438/domain/Enrollment.java index a7f27c7..37c9ed9 100644 --- a/src/main/java/com/cst438/domain/Enrollment.java +++ b/src/main/java/com/cst438/domain/Enrollment.java @@ -11,7 +11,45 @@ public class Enrollment { // TODO complete this class // add additional attribute for grade + private String grade; + // create relationship between enrollment and user entities + @ManyToOne + @JoinColumn(name = "user_id",nullable = false) + private User student; + // create relationship between enrollment and section entities + @ManyToOne + @JoinColumn(name = "section_no", nullable = false) + private Section section; + // add getter/setter methods + + public int getEnrollmentId(){ + return enrollmentId; + } + public void setEnrollmentId(int enrollmentId){ + this.enrollmentId = enrollmentId; + } + + public String getGrade(){ + return grade; + } + public void setGrade(String grade){ + this.grade = grade; + } + + public User getStudent(){ + return student; + } + public void setUser(User student){ + this.student = student; + } + + public Section getSection(){ + return section; + } + public void setSection(Section section){ + this.section = section; + } } diff --git a/src/main/java/com/cst438/domain/EnrollmentRepository.java b/src/main/java/com/cst438/domain/EnrollmentRepository.java index 4ac3267..d68c11d 100644 --- a/src/main/java/com/cst438/domain/EnrollmentRepository.java +++ b/src/main/java/com/cst438/domain/EnrollmentRepository.java @@ -9,15 +9,19 @@ public interface EnrollmentRepository extends CrudRepository findEnrollmentsBySectionNoOrderByStudentName(int sectionNo); -// -// @Query("select e from Enrollment e where e.student.id=:studentId order by e.section.term.termId") -// List findEnrollmentsByStudentIdOrderByTermId(int studentId); -// -// @Query("select e from Enrollment e where e.section.term.year=:year and e.section.term.semester=:semester and e.student.id=:studentId order by e.section.course.courseId") -// List findByYearAndSemesterOrderByCourseId(int year, String semester, int studentId); -// -// @Query("select e from Enrollment e where e.section.sectionNo=:sectionNo and e.student.id=:studentId") -// Enrollment findEnrollmentBySectionNoAndStudentId(int sectionNo, int studentId); + + @Query("select e from Enrollment e where e.section.sectionNo=:sectionNo order by e.student.name") + List findEnrollmentsBySectionNoOrderByStudentName(int sectionNo); + + @Query("select e from Enrollment e where e.student.id=:studentId order by e.section.term.termId") + List findEnrollmentsByStudentIdOrderByTermId(int studentId); + + @Query("select e from Enrollment e where e.section.term.year=:year and e.section.term.semester=:semester and e.student.id=:studentId order by e.section.course.courseId") + List findByYearAndSemesterOrderByCourseId(int year, String semester, int studentId); + + @Query("select e from Enrollment e where e.section.sectionNo=:sectionNo and e.student.id=:studentId") + Enrollment findEnrollmentBySectionNoAndStudentId(int sectionNo, int studentId); + + @Query("select e from Enrollment e where e.enrollmentId=:enrollmentId") + Enrollment findEnrollmentByEnrollmentId(int enrollmentId); } diff --git a/src/main/java/com/cst438/domain/Grade.java b/src/main/java/com/cst438/domain/Grade.java index c5d6514..b7db4dd 100644 --- a/src/main/java/com/cst438/domain/Grade.java +++ b/src/main/java/com/cst438/domain/Grade.java @@ -8,10 +8,29 @@ public class Grade { @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="grade_id") private int gradeId; - + // TODO complete this class - // add additional attribute for score + // add relationship between grade and assignment entities + @ManyToOne + @JoinColumn(name="assignment_id") + private Assignment assignment; // add relationship between grade and enrollment entities + @ManyToOne + @JoinColumn(name="enrollment_id") + private Enrollment enrollment; + // add additional attribute for score + @Column(name="score") + private Integer score; + // add getter/setter methods -} + public int getGradeId() {return gradeId; } + public void setGradeId(int gradeId) {this.gradeId = gradeId; } + public Integer getScore() {return score; } + public void setScore (Integer score) {this.score = score; } + public Assignment getAssignment() {return assignment; } + public void setAssignment(Assignment assignment) {this.assignment = assignment; } + public Enrollment getEnrollment() {return enrollment; } + public void setEnrollment(Enrollment enrollment) {this.enrollment = enrollment; } + +} \ No newline at end of file diff --git a/src/main/java/com/cst438/domain/GradeRepository.java b/src/main/java/com/cst438/domain/GradeRepository.java index ddb6155..78028c1 100644 --- a/src/main/java/com/cst438/domain/GradeRepository.java +++ b/src/main/java/com/cst438/domain/GradeRepository.java @@ -7,6 +7,8 @@ public interface GradeRepository extends CrudRepository { // TODO uncomment the following lines as needed -// @Query("select g from Grade g where g.assignment.assignmentId=:assignmentId and g.enrollment.enrollmentId=:enrollmentId") -// Grade findByEnrollmentIdAndAssignmentId(int enrollmentId, int assignmentId); + @Query("select g from Grade g where g.assignment.assignmentId=:assignmentId and g.enrollment.enrollmentId=:enrollmentId") + Grade findByEnrollmentIdAndAssignmentId(int enrollmentId, int assignmentId); + + } diff --git a/src/main/java/com/cst438/domain/Section.java b/src/main/java/com/cst438/domain/Section.java index 0fe2417..8003345 100644 --- a/src/main/java/com/cst438/domain/Section.java +++ b/src/main/java/com/cst438/domain/Section.java @@ -29,11 +29,11 @@ public class Section { // TODO uncomment the following lines -// @OneToMany(mappedBy="section") -// List enrollments; + @OneToMany(mappedBy="section") + List enrollments; -// @OneToMany(mappedBy="section") -// List assignments; + @OneToMany(mappedBy="section") + List assignments; public int getSectionNo() { return sectionNo; diff --git a/src/main/java/com/cst438/domain/SectionRepository.java b/src/main/java/com/cst438/domain/SectionRepository.java index 6e46cc0..a1b0400 100644 --- a/src/main/java/com/cst438/domain/SectionRepository.java +++ b/src/main/java/com/cst438/domain/SectionRepository.java @@ -18,4 +18,7 @@ public interface SectionRepository extends CrudRepository { @Query("select s from Section s where current_date between s.term.addDate and s.term.addDeadline " + " order by s.course.courseId, s.secId") List
findByOpenOrderByCourseIdSectionId(); + + @Query("select s from Section s where s.sectionNo=:sectionNo") + Section findSectionBySectionNo(int sectionNo); } diff --git a/src/test/java/com/cst438/controller/AssignmentControllerSystemTest.java b/src/test/java/com/cst438/controller/AssignmentControllerSystemTest.java new file mode 100644 index 0000000..42594c7 --- /dev/null +++ b/src/test/java/com/cst438/controller/AssignmentControllerSystemTest.java @@ -0,0 +1,162 @@ +package com.cst438.controller; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + + +public class AssignmentControllerSystemTest { + + //Path to selenium driver + public static final String CHROME_DRIVER_FILE_LOCATION = + "/Users/kyleabsten/Library/Mobile Documents/com~apple~CloudDocs/CSUMB/CST438_SoftwareEngineering/downloads/chromedriver-mac-arm64/chromedriver"; + + //Url of react/nodejs server + public static final String reactURL = "http://localhost:3000"; + + //Sleep variable = 1 second + public static final int SLEEP_DURATION = 1000; + + + WebDriver driver; + + @BeforeEach + public void setUpDriver() throws Exception { + //Set properties required by Chrome driver + System.setProperty( + "webdriver.chrome.driver", CHROME_DRIVER_FILE_LOCATION); + ChromeOptions ops = new ChromeOptions(); + ops.addArguments("--remote-allow-origins=*"); + ops.addArguments("--no-sandbox"); + + //Start driver + driver = new ChromeDriver(ops); + + driver.get(reactURL); + //Short wait to allow page to download + Thread.sleep(SLEEP_DURATION); + + } + + + @AfterEach + public void destroyDriver() { + if (driver != null) { + //Quit driver + driver.close(); + driver.quit(); + driver = null; + } + } + + @Test + public void systemTestGradeAssignment() throws Exception { + //Input the proper year and semester + driver.findElement(By.id("year")).sendKeys("2024"); + driver.findElement(By.id("semester")).sendKeys("Spring"); + driver.findElement(By.linkText("Show Sections")).click(); + Thread.sleep(SLEEP_DURATION); + + //Section 8 should have two assignments + WebElement sec8 = driver.findElement(By.id("8")); + sec8.findElement(By.linkText("View Assignments")).click(); + Thread.sleep(SLEEP_DURATION); + + //We will select assignment one to grade. + WebElement ass1 = driver.findElement(By.id("1")); + ass1.findElements(By.tagName("button")).get(0).click(); + Thread.sleep(SLEEP_DURATION); + + //We will enter a score of 88 for all students + List scoreFields = driver.findElements(By.name("score")); + + for(WebElement field:scoreFields){ + field.clear(); + field.sendKeys("88"); + } + driver.findElement(By.id("saveGrades")).click(); + Thread.sleep(SLEEP_DURATION); + String message = driver.findElement(By.id("editMessage")).getText(); + assertEquals("Grades saved!", message); + driver.findElement(By.id("closeGrades")).click(); + + + + } + + @Test + public void systemTestAddAssignment() throws Exception{ + //Adds an assignment for cst363, Spring 2024 + driver.findElement(By.id("year")).sendKeys("2024"); + driver.findElement(By.id("semester")).sendKeys("Spring"); + driver.findElement(By.linkText("Show Sections")).click(); + Thread.sleep(SLEEP_DURATION); + + //Check for cst363, view assignments + try{ + while(true){ + WebElement row363 = driver.findElement(By.xpath("//tr[td='cst363']")); + List links = row363.findElements(By.tagName("a")); + assertEquals(2, links.size()); + //View Assignments is the second link + links.get(1).click(); + Thread.sleep(SLEEP_DURATION); + } + } catch (NoSuchElementException e){ + //do nothing about it + } + + //Add assginment + driver.findElement(By.id("addAssignment")).click(); + Thread.sleep(SLEEP_DURATION); + + //populate data fields + driver.findElement(By.id("addTitle")).sendKeys("db assignment 3 [TEST]"); + WebElement dueDateInput = driver.findElement(By.xpath("//input[@name='dueDate']")); + Actions actions = new Actions(driver); + +// Click on the input field to activate it + actions.moveToElement(dueDateInput).click().perform(); + +// Send the year + + +// Move the cursor to the month section + actions.sendKeys(Keys.LEFT).perform(); + actions.sendKeys(Keys.LEFT).perform(); + actions.sendKeys("02").perform(); + actions.sendKeys("22").perform(); + actions.sendKeys("2024").perform(); + actions.sendKeys(Keys.TAB).perform(); + driver.findElement(By.id("save")).click(); + Thread.sleep(SLEEP_DURATION); + + //Check to see if the assignment was added then delete + WebElement row363 = driver.findElement(By.xpath("//tr[td='db assignment 3 [TEST]']")); + List buttons = row363.findElements(By.tagName("button")); + assertEquals(3, buttons.size()); + buttons.get(2).click(); + Thread.sleep(SLEEP_DURATION); + List confirmButtons = driver + .findElement(By.className("react-confirm-alert-button-group")) + .findElements(By.tagName("button")); + assertEquals(2, confirmButtons.size()); + confirmButtons.get(0).click(); + Thread.sleep(SLEEP_DURATION); + + //Check to make sure the assignment is deleted + assertThrows(NoSuchElementException.class, () -> + driver.findElement(By.xpath("//tr[td='db assignment 3 [TEST]']"))); + } + +} diff --git a/src/test/java/com/cst438/controller/AssignmentControllerUnitTest.java b/src/test/java/com/cst438/controller/AssignmentControllerUnitTest.java new file mode 100644 index 0000000..330afeb --- /dev/null +++ b/src/test/java/com/cst438/controller/AssignmentControllerUnitTest.java @@ -0,0 +1,297 @@ +package com.cst438.controller; + +import com.cst438.domain.*; +import com.cst438.dto.AssignmentDTO; +import com.cst438.dto.GradeDTO; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.sql.Date; +import java.util.ArrayList; +import java.util.List; + +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.*; + +@AutoConfigureMockMvc +@SpringBootTest +public class AssignmentControllerUnitTest { + + @Autowired + MockMvc mvc; + + @Autowired + SectionRepository sectionRepository; + + @Autowired + AssignmentRepository assignmentRepository; + + @Autowired + TermRepository termRepository; + + @Autowired + CourseRepository courseRepository; + + @Test + public void addAssignment() throws Exception { + MockHttpServletResponse response; + + // Create DTO with data for new assignment + AssignmentDTO assignment = new AssignmentDTO( + 0, + "Final Project", + "2024-05-01", + "cst363", + 1, + 9 + ); + + // Issue an HTTP POST request to add the assignment + response = mvc.perform( + MockMvcRequestBuilders + .post("/assignments") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(assignment))) + .andReturn() + .getResponse(); + + // Check the response code for 200 meaning OK + assertEquals(200, response.getStatus()); + + // Return data converted from String to DTO + AssignmentDTO result = fromJsonString(response.getContentAsString(), AssignmentDTO.class); + + // Primary key should have a non-zero value from the database + assertNotEquals(0, result.id()); + + Assignment a = assignmentRepository.findById(result.id()).orElse(null); + assertNotNull(a); + Date expectedDate = new Date(124, 4, 1); + assertEquals("Final Project", a.getTitle()); + assertEquals(expectedDate, a.getDueDate()); + + // Clean up after test. Issue HTTP DELETE request for assignment + response = mvc.perform( + MockMvcRequestBuilders + .delete("/assignments/" + result.id())) + .andReturn() + .getResponse(); + + assertEquals(200, response.getStatus()); + + a = assignmentRepository.findById(result.id()).orElse(null); + assertNull(a); + } + + @Test + public void addAssignmentFailsDueDatePastEndDate() throws Exception { + MockHttpServletResponse response; + + AssignmentDTO assignment = new AssignmentDTO( + 0, + "Final Project", + "2024-05-20", + "cst363", + 1, + 9 + ); + + // Issue an HTTP POST request to add the assignment + response = mvc.perform( + MockMvcRequestBuilders + .post("/assignments") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(assignment))) + .andReturn() + .getResponse(); + + // Response should be 404, NOT_FOUND + assertEquals(404, response.getStatus()); + + // Check the expected error message + String message = response.getErrorMessage(); + assertEquals("DueDate is after course EndDate ", message); + } + + @Test + public void addAssignmentFailsBadSection() throws Exception { + MockHttpServletResponse response; + + + //Section 58 isn't valid + AssignmentDTO assignment = new AssignmentDTO( + 0, + "Integers: What are they?", + "2024-05-20", + "cst363", + 1, + 58 + ); + + // The post request, and the response + response = mvc.perform( + MockMvcRequestBuilders + .post("/assignments") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(assignment))) + .andReturn() + .getResponse(); + + // We should get a NOT_FOUND 404 error + assertEquals(404, response.getStatus()); + + // Checking the message returned + assertEquals("section not found 58", response.getErrorMessage()); + } + + //Instructor grades an assignment and uploads + @Test + public void gradeAssignmentForEnrolled() throws Exception { + MockHttpServletResponse responseFromGet; + MockHttpServletResponse responseFromPut; + + String getUrl = "/assignments/2/grades"; + responseFromGet = mvc.perform( + MockMvcRequestBuilders + .get(getUrl)) + .andReturn() + .getResponse(); + + //Check the response code for our get request + assertEquals(200, responseFromGet.getStatus()); + + //Map response to GradeDTO object list + ObjectMapper objectMapper = new ObjectMapper(); + List gradeDTOS = objectMapper.readValue( + responseFromGet.getContentAsString(), + TypeFactory.defaultInstance().constructCollectionType(List.class, GradeDTO.class)); + + //Set score on returned grades + List returnGrades = new ArrayList<>(); + for(GradeDTO grade: gradeDTOS){ + GradeDTO newGrade = new GradeDTO( + grade.gradeId(), + grade.studentName(), + grade.studentEmail(), + grade.assignmentTitle(), + grade.courseId(), + grade.sectionId(), + 95 + ); + returnGrades.add(newGrade); + + }; + //Send put request for updated grades + responseFromPut = mvc.perform( + MockMvcRequestBuilders + .put("/grades") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(returnGrades))) + .andReturn() + .getResponse(); + + //Check that response from put is 200 + assertEquals(200, responseFromPut.getStatus()); + + responseFromGet = mvc.perform( + MockMvcRequestBuilders + .get(getUrl)) + .andReturn() + .getResponse(); + + List updatedGrades = objectMapper.readValue( + responseFromGet.getContentAsString(), + TypeFactory.defaultInstance().constructCollectionType(List.class, GradeDTO.class)); + + //Check that the grades in the database match our updated list + assertEquals(returnGrades, updatedGrades); + + //Check that there was an actual update to the grades + assertNotEquals(gradeDTOS, updatedGrades); + + //Set grades back to original state + responseFromPut = mvc.perform( + MockMvcRequestBuilders + .put("/grades") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(gradeDTOS))) + .andReturn() + .getResponse(); + //Check that put worked + assertEquals(200, responseFromPut.getStatus()); + + responseFromGet = mvc.perform( + MockMvcRequestBuilders + .get(getUrl)) + .andReturn() + .getResponse(); + + List resetGrades = objectMapper.readValue( + responseFromGet.getContentAsString(), + TypeFactory.defaultInstance().constructCollectionType(List.class, GradeDTO.class)); + + //Check that grades now match original state + assertEquals(gradeDTOS, resetGrades); + } + + @Test + public void gradeAssignmentButAssignmentIdInvalid() throws Exception { + MockHttpServletResponse response; + + //create assignment with invalid assignmentId + AssignmentDTO assignment = new AssignmentDTO( + 10, + "Final Project", + "2024-05-20", + "cst363", + 1, + 9 + ); + + // Issue an HTTP POST request to add the assignment + response = mvc.perform( + MockMvcRequestBuilders + .get("/assignments/"+assignment.id()+"/grades") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(assignment))) + .andReturn() + .getResponse(); + + // Response should be 404, NOT_FOUND + assertEquals(404, response.getStatus()); + + // Check the expected error message + String message = response.getErrorMessage(); + assertEquals("assignment not found "+assignment.id(), message); + } + + private static String asJsonString(final Object obj) { + try { + return new ObjectMapper().writeValueAsString(obj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static T fromJsonString(String str, Class valueType) { + try { + return new ObjectMapper().readValue(str, valueType); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/cst438/controller/EnrollmentControllerSystemTest.java b/src/test/java/com/cst438/controller/EnrollmentControllerSystemTest.java new file mode 100644 index 0000000..1ab91f8 --- /dev/null +++ b/src/test/java/com/cst438/controller/EnrollmentControllerSystemTest.java @@ -0,0 +1,85 @@ +package com.cst438.controller; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class EnrollmentControllerSystemTest { + + public static final String CHROME_DRIVER_FILE_LOCATION = + "C:/chromedriver-win64/chromedriver.exe"; + + //public static final String CHROME_DRIVER_FILE_LOCATION = + // "~/chromedriver_macOS/chromedriver"; + + public static final String URL = "http://localhost:3000/"; + public static final int SLEEP_DURATION = 1000; // 1 second. + WebDriver driver; + + // these tests assumes that test data does NOT contain any + // sections for course cst499 in 2024 Spring term. + + @BeforeEach + public void setUpDriver() throws Exception { + // set properties required by Chrome Driver + System.setProperty( + "webdriver.chrome.driver", CHROME_DRIVER_FILE_LOCATION); + ChromeOptions ops = new ChromeOptions(); + ops.addArguments("--remote-allow-origins=*"); + + // start the driver + driver = new ChromeDriver(ops); + + driver.get(URL); + // must have a short wait to allow time for the page to download + Thread.sleep(SLEEP_DURATION); + } + @AfterEach + public void terminateDriver() { + if (driver != null) { + // quit driver + driver.close(); + driver.quit(); + driver = null; + } + } + + @Test + public void systemTestEnterFinalGrade() throws Exception { + //Input the proper year and semester + driver.findElement(By.id("year")).sendKeys("2024"); + driver.findElement(By.id("semester")).sendKeys("Spring"); + driver.findElement(By.linkText("Show Sections")).click(); + Thread.sleep(SLEEP_DURATION); + + //Enter section #8 + WebElement sec8 = driver.findElement(By.id("8")); + sec8.findElement(By.linkText("View Enrollments")).click(); + Thread.sleep(SLEEP_DURATION); + + //change grade for all students + List changeGradeButt = driver.findElements(By.name("changer")); + for(WebElement changeGrade:changeGradeButt){ + changeGrade.click(); + WebElement field = driver.findElement(By.name("grade")); + field.clear(); + field.sendKeys("C"); + driver.findElement(By.name("saver")).click(); + } + Thread.sleep(SLEEP_DURATION); + + String message = driver.findElement(By.name("messager")).getText(); + assertEquals("Grade saved", message); + + } +} diff --git a/src/test/java/com/cst438/controller/EnrollmentControllerUnitTest.java b/src/test/java/com/cst438/controller/EnrollmentControllerUnitTest.java new file mode 100644 index 0000000..6bc2c51 --- /dev/null +++ b/src/test/java/com/cst438/controller/EnrollmentControllerUnitTest.java @@ -0,0 +1,105 @@ +package com.cst438.controller; + +import java.util.ArrayList; +import java.util.List; +import com.cst438.domain.*; +import com.cst438.dto.AssignmentDTO; +import com.cst438.dto.EnrollmentDTO; +import com.cst438.dto.SectionDTO; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.sql.Date; + +import static org.junit.jupiter.api.Assertions.*; + +@AutoConfigureMockMvc +@SpringBootTest +public class EnrollmentControllerUnitTest { + + @Autowired + MockMvc mvc; + + @Autowired + SectionRepository sectionRepository; + + @Autowired + AssignmentRepository assignmentRepository; + + @Autowired + TermRepository termRepository; + + @Autowired + CourseRepository courseRepository; + + @Autowired + EnrollmentRepository enrollmentRepository; + + @Test + public void enterFinalClassGradesForAll() throws Exception{ + MockHttpServletResponse response; + + // Create DTO with data for new assignment + EnrollmentDTO enrollment0 = new EnrollmentDTO( + 1, + "B", + 3, + "thomas edison", + "tedison@csumb.edu", + "cst363", + 9, + 9, + "052", + "104", + "M W 10:00-11:50", + 4, + 2024, + "Spring" + ); + + Listenrollments = new ArrayList<>(); + enrollments.add(enrollment0); + + + + // issue a http POST request to SpringTestServer + // specify MediaType for request and response data + // convert section to String data and set as request content + response = mvc.perform( + MockMvcRequestBuilders + .put("/enrollments") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(enrollments))) + .andReturn() + .getResponse(); + + // check the response code for 200 meaning OK + assertEquals(200, response.getStatus()); + + + } + + private static String asJsonString(final Object obj) { + try { + return new ObjectMapper().writeValueAsString(obj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static T fromJsonString(String str, Class valueType ) { + try { + return new ObjectMapper().readValue(str, valueType); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/cst438/controller/SectionControllerSystemTest.java b/src/test/java/com/cst438/controller/SectionControllerSystemTest.java index 006b969..61bf082 100644 --- a/src/test/java/com/cst438/controller/SectionControllerSystemTest.java +++ b/src/test/java/com/cst438/controller/SectionControllerSystemTest.java @@ -18,7 +18,7 @@ public class SectionControllerSystemTest { // for WinOS the file name will be chromedriver.exe // for MacOS the file name will be chromedriver public static final String CHROME_DRIVER_FILE_LOCATION = - "C:/chromedriver_win32/chromedriver.exe"; + "C:/chromedriver_win64/chromedriver.exe"; //public static final String CHROME_DRIVER_FILE_LOCATION = // "~/chromedriver_macOS/chromedriver"; diff --git a/src/test/java/com/cst438/controller/SectionControllerUnitTest.java b/src/test/java/com/cst438/controller/SectionControllerUnitTest.java index 0d3844e..7f8f2e2 100644 --- a/src/test/java/com/cst438/controller/SectionControllerUnitTest.java +++ b/src/test/java/com/cst438/controller/SectionControllerUnitTest.java @@ -122,7 +122,7 @@ public void addSectionFailsBadCourse( ) throws Exception { .getResponse(); // response should be 400, BAD_REQUEST - assertEquals(400, response.getStatus()); + assertEquals(404, response.getStatus()); // check the expected error message String message = response.getErrorMessage(); diff --git a/src/test/java/com/cst438/controller/StudentControllerSystemTest.java b/src/test/java/com/cst438/controller/StudentControllerSystemTest.java new file mode 100644 index 0000000..d1da47b --- /dev/null +++ b/src/test/java/com/cst438/controller/StudentControllerSystemTest.java @@ -0,0 +1,114 @@ +//note: requires user to be STUDENT + +package com.cst438.controller; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class StudentControllerSystemTest { + + public static final String CHROME_DRIVER_FILE_LOCATION = + "./chromedriver"; + + //public static final String CHROME_DRIVER_FILE_LOCATION = + // "~/chromedriver_macOS/chromedriver"; + public static final String URL = "http://localhost:3000/"; + + public static final int SLEEP_DURATION = 1000; // 1 second. + + // add selenium dependency to pom.xml + + // these tests assumes that test data does NOT contain any + // sections for course cst499 in 2024 Spring term. + + WebDriver driver; + + @BeforeEach + public void setUpDriver() throws Exception { + + // set properties required by Chrome Driver + System.setProperty( + "webdriver.chrome.driver", CHROME_DRIVER_FILE_LOCATION); + ChromeOptions ops = new ChromeOptions(); + ops.addArguments("--remote-allow-origins=*"); + + // start the driver + driver = new ChromeDriver(ops); + + driver.get(URL); + // must have a short wait to allow time for the page to download + Thread.sleep(SLEEP_DURATION); + + } + + @AfterEach + public void terminateDriver() { + if (driver != null) { + // quit driver + driver.close(); + driver.quit(); + driver = null; + } + } + + @Test + public void systemTestEnrollSection() throws Exception { + // add first available section + // verify enrollment appears on schedule + // drop course + + //click link to navigate to course enroll + WebElement we = driver.findElement(By.id("addCourse")); + we.click(); + Thread.sleep(SLEEP_DURATION); + + //connect driver to buttons + List buttons = driver.findElements(By.tagName("button")); + + //consider clicking each available section +// for (WebElement button : buttons) { +// button.click(); +// Thread.sleep(SLEEP_DURATION); +// } + + //click to enroll in first available section + buttons.get(0).click(); + Thread.sleep(SLEEP_DURATION); + + String message = driver.findElement(By.id("addMessage")).getText(); + //assertTrue(message.equals("course added")); + assertEquals("course added", message); + + we = driver.findElement(By.id("schedule")); + we.click(); + driver.findElement(By.id("ayear")).sendKeys("2024"); + driver.findElement(By.id("asemester")).sendKeys("Spring"); + driver.findElement(By.id("search")).click(); + Thread.sleep(SLEEP_DURATION); + + //drop recently added section from CST338 + WebElement row = driver.findElement(By.xpath("//tr[td='cst338']")); + assertNotNull(row); + + WebElement dropButton = row.findElement(By.tagName("button")); + dropButton.click(); + Thread.sleep(SLEEP_DURATION); + + // find the YES to confirm button + List confirmButtons = driver + .findElement(By.className("react-confirm-alert-button-group")) + .findElements(By.tagName("button")); + assertEquals(2,confirmButtons.size()); + confirmButtons.get(0).click(); + Thread.sleep(SLEEP_DURATION); + + } +} diff --git a/src/test/java/com/cst438/controller/StudentControllerUnitTest.java b/src/test/java/com/cst438/controller/StudentControllerUnitTest.java new file mode 100644 index 0000000..511aa91 --- /dev/null +++ b/src/test/java/com/cst438/controller/StudentControllerUnitTest.java @@ -0,0 +1,250 @@ +package com.cst438.controller; + +import com.cst438.domain.*; +import com.cst438.dto.AssignmentDTO; +import com.cst438.dto.EnrollmentDTO; +import com.cst438.dto.SectionDTO; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.sql.Date; + +import static org.junit.jupiter.api.Assertions.*; + +@AutoConfigureMockMvc +@SpringBootTest +public class StudentControllerUnitTest { + + @Autowired + MockMvc mvc; + + @Autowired + SectionRepository sectionRepository; + + @Autowired + AssignmentRepository assignmentRepository; + + @Autowired + TermRepository termRepository; + + @Autowired + CourseRepository courseRepository; + + @Autowired + EnrollmentRepository enrollmentRepository; + + @Test + public void enrollInSection() throws Exception { + MockHttpServletResponse response; + + // Create DTO with data for new assignment + EnrollmentDTO enrollment = new EnrollmentDTO( + 0, + "A", + 3, + "thomas edison", + "tedison@csumb.edu", + "cst363", + 9, + 9, + "052", + "104", + "M W 10:00-11:50", + 4, + 2024, + "Spring" + ); + + // issue a http POST request to SpringTestServer + // specify MediaType for request and response data + // convert section to String data and set as request content + response = mvc.perform( + MockMvcRequestBuilders + .post("/enrollments/sections/"+enrollment.sectionNo()+"?studentId="+enrollment.studentId()) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(enrollment))) + .andReturn() + .getResponse(); + + // check the response code for 200 meaning OK + assertEquals(200, response.getStatus()); + + // return data converted from String to DTO + EnrollmentDTO result = fromJsonString(response.getContentAsString(), EnrollmentDTO.class); + + // primary key should have a non zero value from the database + assertNotEquals(0, result.enrollmentId()); + // check other fields of the DTO for expected values + assertEquals("cst363", result.courseId()); + + // check the database + Enrollment e = enrollmentRepository.findById(result.enrollmentId()).orElse(null); + assertNotNull(e); + assertEquals("cst363", e.getSection().getCourse().getCourseId()); + + // clean up after test. issue http DELETE request for section + response = mvc.perform( + MockMvcRequestBuilders + .delete("/enrollments/"+result.enrollmentId())) + .andReturn() + .getResponse(); + + assertEquals(200, response.getStatus()); + + // check database for delete + e = enrollmentRepository.findById(result.enrollmentId()).orElse(null); + //s = sectionRepository.findById(result.secNo()).orElse(null); + assertNull(e); // section should not be found after delete + } + + @Test + public void enrollInSectionButAlreadyEnrolled() throws Exception { + MockHttpServletResponse response; + + // Create DTO with data for new assignment + EnrollmentDTO enrollment = new EnrollmentDTO( + 0, + "A", + 3, + "thomas edison", + "tedison@csumb.edu", + "cst363", + 1, + 8, + "052", + "104", + "M W 10:00-11:50", + 4, + 2024, + "Spring" + ); + + // issue a http POST request to SpringTestServer + // specify MediaType for request and response data + // convert section to String data and set as request content + response = mvc.perform( + MockMvcRequestBuilders + .post("/enrollments/sections/"+enrollment.sectionNo()+"?studentId="+enrollment.studentId()) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(enrollment))) + .andReturn() + .getResponse(); + + // check the response code for 200 meaning OK + assertEquals(400, response.getStatus()); + + // check the expected error message + String message = response.getErrorMessage(); + assertEquals("already enrolled in this section", message); + } + + @Test + public void enrollinSectionButInvalidSectionNo() throws Exception{ + MockHttpServletResponse response; + + // Create DTO with data for new assignment + EnrollmentDTO enrollment = new EnrollmentDTO( + 0, + "A", + 3, + "thomas edison", + "tedison@csumb.edu", + "cst363", + 1, + 58, + "052", + "104", + "M W 10:00-11:50", + 4, + 2024, + "Spring" + ); + + // issue a http POST request to SpringTestServer + // specify MediaType for request and response data + // convert section to String data and set as request content + response = mvc.perform( + MockMvcRequestBuilders + .post("/enrollments/sections/"+enrollment.sectionNo()+"?studentId="+enrollment.studentId()) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(enrollment))) + .andReturn() + .getResponse(); + + // We should get a NOT_FOUND 404 error + assertEquals(404, response.getStatus()); + + // check the expected error message + String message = response.getErrorMessage(); + assertEquals("section not found 58", message); + + } + + @Test + public void enrollinSectionButPastDeadline() throws Exception{ + MockHttpServletResponse response; + + // Create DTO with data for new assignment + EnrollmentDTO enrollment = new EnrollmentDTO( + 0, + "A", + 3, + "thomas edison", + "tedison@csumb.edu", + "cst338", + 1, + 1, + "052", + "100", + "M W 10:00-11:50", + 4, + 2024, + "Spring" + ); + + // issue a http POST request to SpringTestServer + // specify MediaType for request and response data + // convert section to String data and set as request content + response = mvc.perform( + MockMvcRequestBuilders + .post("/enrollments/sections/"+enrollment.sectionNo()+"?studentId="+enrollment.studentId()) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(enrollment))) + .andReturn() + .getResponse(); + + // check the response code for 200 meaning OK + assertEquals(400, response.getStatus()); + + // check the expected error message + String message = response.getErrorMessage(); + assertEquals("Invalid date", message); + } + + private static String asJsonString(final Object obj) { + try { + return new ObjectMapper().writeValueAsString(obj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static T fromJsonString(String str, Class valueType ) { + try { + return new ObjectMapper().readValue(str, valueType); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +}