001package edu.pdx.cs.joy.grader; 002 003import edu.pdx.cs.joy.grader.gradebook.Assignment; 004import edu.pdx.cs.joy.grader.gradebook.Grade; 005import edu.pdx.cs.joy.grader.gradebook.GradeBook; 006import edu.pdx.cs.joy.grader.gradebook.Student; 007import org.hamcrest.Description; 008import org.hamcrest.Matcher; 009import org.hamcrest.TypeSafeMatcher; 010import org.junit.jupiter.api.Test; 011 012import java.io.File; 013import java.time.LocalDateTime; 014import java.util.List; 015import java.util.Optional; 016import java.util.jar.Attributes; 017import java.util.jar.Manifest; 018 019import static org.hamcrest.MatcherAssert.assertThat; 020import static org.hamcrest.Matchers.*; 021import static org.junit.jupiter.api.Assertions.assertThrows; 022 023public class ProjectSubmissionsProcessorTest { 024 025 /** 026 * Given: A submission from a student whose name is in the grade book, 027 * but whose id is not 028 * 029 * When: The submission is recorded 030 * 031 * Then: The submission is noted under the expected id and a new Student 032 * is <b>not</b> created. 033 * 034 * This tests issue #52 (https://github.com/JoyOfCodingPDX/JoyOfCoding/issues/52) 035 */ 036 @Test 037 public void matchStudentBasedOnFirstAndLastName() throws StudentEmailAttachmentProcessor.SubmissionException { 038 String projectName = "Project"; 039 040 GradeBook gradebook = createGradeBookWithAssignment(projectName); 041 Student student = createStudentInGradeBook(gradebook); 042 043 String studentName = student.getFirstName() + " " + student.getLastName(); 044 String wrongStudentId = "Not the student id we expect"; 045 String wrongEmail = "Not the email that we expect"; 046 047 Manifest manifest = manifest(projectName) 048 .setStudentName(studentName) 049 .setStudentId(wrongStudentId) 050 .setStudentEmail(wrongEmail) 051 .build(); 052 053 noteProjectSubmissionInGradeBook(gradebook, manifest); 054 055 assertThat(gradebook.getStudent(wrongStudentId), isNotPresent()); 056 057 assertThatProjectSubmissionWasRecordedForStudent(projectName, student); 058 } 059 060 @Test 061 public void matchStudentBasedOnNickAndLastName() throws StudentEmailAttachmentProcessor.SubmissionException { 062 String projectName = "Project"; 063 064 GradeBook gradebook = createGradeBookWithAssignment(projectName); 065 Student student = createStudentInGradeBook(gradebook); 066 067 String studentName = student.getNickName() + " " + student.getLastName(); 068 String wrongStudentId = "Not the student id we expect"; 069 String wrongEmail = "Not the email that we expect"; 070 071 Manifest manifest = manifest(projectName) 072 .setStudentName(studentName) 073 .setStudentId(wrongStudentId) 074 .setStudentEmail(wrongEmail) 075 .build(); 076 077 noteProjectSubmissionInGradeBook(gradebook, manifest); 078 079 assertThat(gradebook.getStudent(wrongStudentId), isNotPresent()); 080 081 assertThatProjectSubmissionWasRecordedForStudent(projectName, student); 082 } 083 084 @Test 085 public void matchStudentBasedOnEmail() throws StudentEmailAttachmentProcessor.SubmissionException { 086 String projectName = "Project"; 087 088 GradeBook gradebook = createGradeBookWithAssignment(projectName); 089 Student student = createStudentInGradeBook(gradebook); 090 091 String studentName = "Not the student name we expect"; 092 String wrongStudentId = "Not the student id we expect"; 093 String email = student.getEmail(); 094 095 Manifest manifest = manifest(projectName) 096 .setStudentName(studentName) 097 .setStudentId(wrongStudentId) 098 .setStudentEmail(email) 099 .build(); 100 101 noteProjectSubmissionInGradeBook(gradebook, manifest); 102 103 assertThat(gradebook.getStudent(wrongStudentId), isNotPresent()); 104 105 assertThatProjectSubmissionWasRecordedForStudent(projectName, student); 106 } 107 108 @Test 109 public void submissionDoesNotMatchAnyStudentInGradeBook() { 110 String projectName = "Project"; 111 112 GradeBook gradebook = createGradeBookWithAssignment(projectName); 113 114 String studentName = "Not the student name we expect"; 115 String wrongStudentId = "Not the student id we expect"; 116 String wrongEmail = "Not the email we expect"; 117 118 Manifest manifest = manifest(projectName) 119 .setStudentName(studentName) 120 .setStudentId(wrongStudentId) 121 .setStudentEmail(wrongEmail) 122 .build(); 123 124 assertThrows(StudentEmailAttachmentProcessor.SubmissionException.class, () -> 125 noteProjectSubmissionInGradeBook(gradebook, manifest) 126 ); 127 } 128 129 @Test 130 public void submissionTimeNotedInGradeBook() throws StudentEmailAttachmentProcessor.SubmissionException { 131 String projectName = "Project"; 132 133 GradeBook gradebook = createGradeBookWithAssignment(projectName); 134 Student student = createStudentInGradeBook(gradebook); 135 136 LocalDateTime submissionDate = LocalDateTime.now().minusHours(2).withNano(0); 137 Manifest manifest = manifest(projectName) 138 .setStudent(student) 139 .setSubmissionDate(submissionDate) 140 .build(); 141 142 noteProjectSubmissionInGradeBook(gradebook, manifest); 143 144 assertThat(student.getGrade(projectName).getSubmissionTimes(), contains(submissionDate)); 145 } 146 147 @Test 148 public void estimatedHoursNotedInGradeBook() throws StudentEmailAttachmentProcessor.SubmissionException { 149 String projectName = "Project"; 150 151 GradeBook gradebook = createGradeBookWithAssignment(projectName); 152 Student student = createStudentInGradeBook(gradebook); 153 154 Double estimatedHours = 4.5; 155 Manifest manifest = manifest(projectName) 156 .setStudent(student) 157 .setEstimatedHours(estimatedHours) 158 .build(); 159 160 noteProjectSubmissionInGradeBook(gradebook, manifest); 161 162 List<Grade.SubmissionInfo> submissions = student.getGrade(projectName).getSubmissionInfos(); 163 assertThat(submissions, hasSize(1)); 164 assertThat(submissions.get(0).getEstimatedHours(), equalTo(estimatedHours)); 165 } 166 167 @Test 168 public void submissionsPastDueDateAreLate() throws StudentEmailAttachmentProcessor.SubmissionException { 169 String projectName = "Project"; 170 LocalDateTime dueDate = LocalDateTime.now(); 171 172 GradeBook gradebook = createGradeBookWithAssignment(projectName, dueDate); 173 Student student = createStudentInGradeBook(gradebook); 174 175 LocalDateTime submissionDate = dueDate.plusDays(3); 176 Manifest manifest = manifest(projectName) 177 .setStudent(student) 178 .setSubmissionDate(submissionDate) 179 .build(); 180 181 noteProjectSubmissionInGradeBook(gradebook, manifest); 182 183 assertThat(student.getLate(), contains(projectName)); 184 185 Grade grade = student.getGrade(projectName); 186 assertThat(grade, notNullValue()); 187 List<Grade.SubmissionInfo> submissions = grade.getSubmissionInfos(); 188 assertThat(submissions, hasSize(1)); 189 Grade.SubmissionInfo submission = submissions.get(0); 190 assertThat(submission.getSubmissionTime(), equalTo(submissionDate)); 191 assertThat(submission.isLate(), equalTo(true)); 192 } 193 194 @Test 195 public void submissionsBeforeDueDateAreNoteLate() throws StudentEmailAttachmentProcessor.SubmissionException { 196 String projectName = "Project"; 197 LocalDateTime dueDate = LocalDateTime.now(); 198 199 GradeBook gradebook = createGradeBookWithAssignment(projectName, dueDate); 200 Student student = createStudentInGradeBook(gradebook); 201 202 LocalDateTime submissionDate = dueDate.minusDays(3); 203 Manifest manifest = manifest(projectName) 204 .setStudent(student) 205 .setSubmissionDate(submissionDate) 206 .build(); 207 208 noteProjectSubmissionInGradeBook(gradebook, manifest); 209 210 assertThat(student.getLate(), not(contains(projectName))); 211 212 Grade grade = student.getGrade(projectName); 213 assertThat(grade, notNullValue()); 214 List<Grade.SubmissionInfo> submissions = grade.getSubmissionInfos(); 215 assertThat(submissions, hasSize(1)); 216 Grade.SubmissionInfo submission = submissions.get(0); 217 assertThat(submission.getSubmissionTime(), equalTo(submissionDate)); 218 assertThat(submission.isLate(), equalTo(false)); 219 } 220 221 private void noteProjectSubmissionInGradeBook(GradeBook gradebook, Manifest manifest) throws StudentEmailAttachmentProcessor.SubmissionException { 222 ProjectSubmissionsProcessor processor = 223 new ProjectSubmissionsProcessor(new File(System.getProperty("user.dir")), gradebook); 224 processor.noteSubmissionInGradeBook(manifest); 225 } 226 227 private void assertThatProjectSubmissionWasRecordedForStudent(String projectName, Student student) { 228 Grade grade = student.getGrade(projectName); 229 assertThat(grade, not(nullValue())); 230 assertThat(grade.isNotGraded(), equalTo(true)); 231 } 232 233 private Student createStudentInGradeBook(GradeBook gradebook) { 234 Student student = new Student("studentId"); 235 student.setFirstName("firstName"); 236 student.setLastName("lastName"); 237 student.setNickName("nickName"); 238 student.setEmail("test@test.com"); 239 gradebook.addStudent(student); 240 return student; 241 } 242 243 private GradeBook createGradeBookWithAssignment(String projectName) { 244 return createGradeBookWithAssignment(projectName, null); 245 } 246 247 private GradeBook createGradeBookWithAssignment(String projectName, LocalDateTime dueDate) { 248 GradeBook gradebook = new GradeBook("test"); 249 Assignment assignment = new Assignment(projectName, 10.0); 250 assignment.setDueDate(dueDate); 251 gradebook.addAssignment(assignment); 252 return gradebook; 253 } 254 255 // Test match on email and nickname 256 257 private static Matcher<? super Optional<Student>> isNotPresent() { 258 return new TypeSafeMatcher<>() { 259 @Override 260 protected boolean matchesSafely(Optional<Student> item) { 261 return item.isEmpty(); 262 } 263 264 @Override 265 public void describeTo(Description description) { 266 description.appendText("an Optional<Student> that is not present"); 267 } 268 }; 269 } 270 271 private ManifestBuilder manifest(String projectName) { 272 return new ManifestBuilder() 273 .setProjectName(projectName) 274 .setSubmissionDate(LocalDateTime.now()) 275 .setSubmissionComment("Why is a comment required?") 276 ; 277 } 278 279 private static class ManifestBuilder { 280 Manifest manifest = new Manifest(); 281 282 private ManifestBuilder setAttribute(Attributes.Name name, String value) { 283 this.manifest.getMainAttributes().put(name, value); 284 return this; 285 } 286 287 Manifest build() { 288 return manifest; 289 } 290 291 private ManifestBuilder setProjectName(String projectName) { 292 return setAttribute(Submit.ManifestAttributes.PROJECT_NAME, projectName); 293 } 294 295 public ManifestBuilder setStudent(Student student) { 296 return setStudentId(student.getId()) 297 .setStudentName(student.getFullName()) 298 .setStudentEmail(student.getEmail()) 299 ; 300 } 301 302 private ManifestBuilder setStudentId(String studentId) { 303 return setAttribute(Submit.ManifestAttributes.USER_ID, studentId); 304 } 305 306 public ManifestBuilder setStudentName(String studentName) { 307 return setAttribute(Submit.ManifestAttributes.USER_NAME, studentName); 308 } 309 310 public ManifestBuilder setStudentEmail(String studentEmail) { 311 return setAttribute(Submit.ManifestAttributes.USER_EMAIL, studentEmail); 312 } 313 314 public ManifestBuilder setSubmissionDate(LocalDateTime submissionDate) { 315 return setSubmissionDate(Submit.ManifestAttributes.formatSubmissionTime(submissionDate)); 316 } 317 318 private ManifestBuilder setSubmissionDate(String submissionDate) { 319 return setAttribute(Submit.ManifestAttributes.SUBMISSION_TIME, submissionDate); 320 } 321 322 public ManifestBuilder setSubmissionComment(String submissionComment) { 323 return setAttribute(Submit.ManifestAttributes.SUBMISSION_COMMENT, submissionComment); 324 } 325 326 public ManifestBuilder setEstimatedHours(Double estimatedHours) { 327 return setAttribute(Submit.ManifestAttributes.ESTIMATED_HOURS, String.valueOf(estimatedHours)); 328 } 329 } 330 331}