Skip to content

Commit

Permalink
Feat(Class, Evaluation): Evaluate class use case
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur-Poffo committed Feb 10, 2024
1 parent 02961f2 commit d5865a5
Show file tree
Hide file tree
Showing 9 changed files with 418 additions and 20 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
- [x] After completing a course, the student can issue a certificate.

- [x] It should be possible to filter courses by name or "tags."
- [ ] Students can evaluate a particular course they are taking with a rating from 1 to 5 to later have an average rating for each course.
- [x] Students can evaluate a particular class they are taking with a rating from 1 to 5 to later have an average rating for the course.
- [ ] get average of course evaluations

- [ ] An instructor should be able to retrieve traffic data for one of their courses.

Expand All @@ -42,15 +43,15 @@
- [x] User should not be able to register with the same email.
- [x] User should not be able to register with the same CPF.
- [x] Only students can enroll for a course.
- [ ] Only students can rate courses and classes.
- [x] Only students can rate classes.
- [x] Only instructors can register a course.
- [x] A specific instructor cannot register a course with the same name.
- [ ] A student should only be able to rate a class and a given course once.
- [x] A student should only be able to rate a class once.
- [x] Should not be able to register a course with the same name in same instructor account.
- [x] Should not be able to register a module to a course with the same name twice.
- [x] Should not be able to add a class to a module with the same name twice.
- [x] Should not be able to register a module to a specific course with same name twice.
- [ ] A student can only issue one certificate per course.
- [x] A student can only issue one certificate per course.
- [x] A student can only enroll for a particular course once.
- [x] There should not be repeated tags in a course.

Expand Down Expand Up @@ -115,11 +116,10 @@
- ocurredAt: date
- completedAt: date | null

- [x] Rate
- [x] Evaluation
- - id: string
- value: number (1 - 5)
- userId: string
- courseId: string | null
- classId: string | null
- createdAt: date

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type Evaluation } from '../../enterprise/entities/evaluation'

export interface EvaluationsRepository {
findById: (id: string) => Promise<Evaluation | null>
findByStudentIdAndClassId: (studentId: string, classId: string) => Promise<Evaluation | null>
create: (evaluation: Evaluation) => Promise<Evaluation>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type UseCaseError } from '@/core/errors/use-case-error'

export class InvalidEvaluationValueError extends Error implements UseCaseError {
constructor() {
super('Invalid evaluation value.')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type UseCaseError } from '@/core/errors/use-case-error'

export class StudentAlreadyEvaluateThisClassError extends Error implements UseCaseError {
constructor(className: string) {
super(`Student already evaluate the class: ${className}.`)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type UseCaseError } from '@/core/errors/use-case-error'

export class StudentMustBeRegisteredToEvaluateError extends Error implements UseCaseError {
constructor(courseName: string) {
super(`Student must be registered in ${courseName} to evaluate.`)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
import { ResourceNotFoundError } from '@/core/errors/errors/resource-not-found-error'
import { makeClass } from '../../../../../test/factories/make-class'
import { makeCourse } from '../../../../../test/factories/make-course'
import { makeEnrollment } from '../../../../../test/factories/make-enrollment'
import { makeInstructor } from '../../../../../test/factories/make-instructor'
import { makeModule } from '../../../../../test/factories/make-module'
import { makeStudent } from '../../../../../test/factories/make-student'
import { InMemoryClassesRepository } from '../../../../../test/repositories/in-memory-classes-repository'
import { type InMemoryCourseTagsRepository } from '../../../../../test/repositories/in-memory-course-tags-repository'
import { InMemoryCoursesRepository } from '../../../../../test/repositories/in-memory-courses-repository'
import { InMemoryEnrollmentsRepository } from '../../../../../test/repositories/in-memory-enrollments-repository'
import { InMemoryEvaluationsRepository } from '../../../../../test/repositories/in-memory-evaluations-repository'
import { InMemoryStudentsRepository } from '../../../../../test/repositories/in-memory-students-repository'
import { InMemoryInstructorRepository } from './../../../../../test/repositories/in-memory-instructors-repository'
import { InMemoryModulesRepository } from './../../../../../test/repositories/in-memory-modules-repository'
import { InvalidEvaluationValueError } from './errors/invalid-evaluation-value-error'
import { StudentAlreadyEvaluateThisClassError } from './errors/student-already-evaluate-this-class-error'
import { StudentMustBeRegisteredToEvaluateError } from './errors/student-must-be-registered-to-evaluate-error'
import { EvaluateClassUseCase } from './evaluate-class'

let inMemoryEnrollmentsRepository: InMemoryEnrollmentsRepository
let inMemoryEvaluationsRepository: InMemoryEvaluationsRepository
let inMemoryCourseTagsRepository: InMemoryCourseTagsRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryStudentsRepository: InMemoryStudentsRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let sut: EvaluateClassUseCase

describe('Evaluate class use case', () => {
beforeEach(() => {
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryEvaluationsRepository = new InMemoryEvaluationsRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()
inMemoryStudentsRepository = new InMemoryStudentsRepository()

inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)

inMemoryEnrollmentsRepository = new InMemoryEnrollmentsRepository(
inMemoryClassesRepository, inMemoryModulesRepository
)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository, inMemoryEnrollmentsRepository, inMemoryStudentsRepository, inMemoryCourseTagsRepository)

sut = new EvaluateClassUseCase(
inMemoryEvaluationsRepository,
inMemoryStudentsRepository,
inMemoryCoursesRepository,
inMemoryClassesRepository,
inMemoryEnrollmentsRepository
)
})

it('should be able to evaluate a class', async () => {
const instructor = makeInstructor()
await inMemoryInstructorsRepository.create(instructor)

const course = makeCourse({ instructorId: instructor.id })
await inMemoryCoursesRepository.create(course)

const module = makeModule({
courseId: course.id,
moduleNumber: 1
})
await inMemoryModulesRepository.create(module)

const classToEvaluate = makeClass({ name: 'John Doe Class', moduleId: module.id, classNumber: 1 })
await inMemoryClassesRepository.create(classToEvaluate)

const student = makeStudent()
await inMemoryStudentsRepository.create(student)

const enrollment = makeEnrollment({ courseId: course.id, studentId: student.id })
await inMemoryEnrollmentsRepository.create(enrollment)

const result = await sut.exec({
value: 3,
studentId: student.id.toString(),
courseId: course.id.toString(),
classId: classToEvaluate.id.toString()
})

expect(result.isRight()).toBe(true)
expect(result.value).toMatchObject({
evaluation: expect.objectContaining({
value: 3,
studentId: student.id,
classId: classToEvaluate.id
})
})
})

it('should not be able to evaluate a class with an invalid value', async () => {
const instructor = makeInstructor()
await inMemoryInstructorsRepository.create(instructor)

const course = makeCourse({ instructorId: instructor.id })
await inMemoryCoursesRepository.create(course)

const module = makeModule({
courseId: course.id,
moduleNumber: 1
})
await inMemoryModulesRepository.create(module)

const classToEvaluate = makeClass({ name: 'John Doe Class', moduleId: module.id, classNumber: 1 })
await inMemoryClassesRepository.create(classToEvaluate)

const student = makeStudent()
await inMemoryStudentsRepository.create(student)

const enrollment = makeEnrollment({ courseId: course.id, studentId: student.id })
await inMemoryEnrollmentsRepository.create(enrollment)

const result = await sut.exec({
value: 7, // The rating must be from 1 to 5
studentId: student.id.toString(),
courseId: course.id.toString(),
classId: classToEvaluate.id.toString()
})

expect(result.isLeft()).toBe(true)
expect(result.value).toBeInstanceOf(InvalidEvaluationValueError)
})

it('should not be able to evaluate a inexistent class', async () => {
const instructor = makeInstructor()
await inMemoryInstructorsRepository.create(instructor)

const course = makeCourse({ instructorId: instructor.id })
await inMemoryCoursesRepository.create(course)

const module = makeModule({
courseId: course.id,
moduleNumber: 1
})
await inMemoryModulesRepository.create(module)

const student = makeStudent()
await inMemoryStudentsRepository.create(student)

const enrollment = makeEnrollment({ courseId: course.id, studentId: student.id })
await inMemoryEnrollmentsRepository.create(enrollment)

const result = await sut.exec({
value: 3,
studentId: student.id.toString(),
courseId: course.id.toString(),
classId: 'inexistentClassId'
})

expect(result.isLeft()).toBe(true)
expect(result.value).toBeInstanceOf(ResourceNotFoundError)
})

it('an inexistent student should not be able to evaluate a class', async () => {
const instructor = makeInstructor()
await inMemoryInstructorsRepository.create(instructor)

const course = makeCourse({ instructorId: instructor.id })
await inMemoryCoursesRepository.create(course)

const module = makeModule({
courseId: course.id,
moduleNumber: 1
})
await inMemoryModulesRepository.create(module)

const classToEvaluate = makeClass({ name: 'John Doe Class', moduleId: module.id, classNumber: 1 })
await inMemoryClassesRepository.create(classToEvaluate)

const result = await sut.exec({
value: 3,
studentId: 'inexistentStudentId',
courseId: course.id.toString(),
classId: classToEvaluate.id.toString()
})

expect(result.isLeft()).toBe(true)
expect(result.value).toBeInstanceOf(ResourceNotFoundError)
})

it('an student should not be able to evaluate a same class twice', async () => {
const instructor = makeInstructor()
await inMemoryInstructorsRepository.create(instructor)

const course = makeCourse({ instructorId: instructor.id })
await inMemoryCoursesRepository.create(course)

const module = makeModule({
courseId: course.id,
moduleNumber: 1
})
await inMemoryModulesRepository.create(module)

const classToEvaluate = makeClass({ name: 'John Doe Class', moduleId: module.id, classNumber: 1 })
await inMemoryClassesRepository.create(classToEvaluate)

const student = makeStudent()
await inMemoryStudentsRepository.create(student)

const enrollment = makeEnrollment({ courseId: course.id, studentId: student.id })
await inMemoryEnrollmentsRepository.create(enrollment)

await sut.exec({
value: 3,
studentId: student.id.toString(),
courseId: course.id.toString(),
classId: classToEvaluate.id.toString()
})

const result = await sut.exec({
value: 3,
studentId: student.id.toString(),
courseId: course.id.toString(),
classId: classToEvaluate.id.toString()
})

expect(result.isLeft()).toBe(true)
expect(result.value).toBeInstanceOf(StudentAlreadyEvaluateThisClassError)
})

it('should not be able to evaluate a class if the student is not enrolled in the respective course', async () => {
const instructor = makeInstructor()
await inMemoryInstructorsRepository.create(instructor)

const course = makeCourse({ instructorId: instructor.id })
await inMemoryCoursesRepository.create(course)

const module = makeModule({
courseId: course.id,
moduleNumber: 1
})
await inMemoryModulesRepository.create(module)

const classToEvaluate = makeClass({ name: 'John Doe Class', moduleId: module.id, classNumber: 1 })
await inMemoryClassesRepository.create(classToEvaluate)

const student = makeStudent()
await inMemoryStudentsRepository.create(student)

// Not enrolled in the course

const result = await sut.exec({
value: 3,
studentId: student.id.toString(),
courseId: course.id.toString(),
classId: classToEvaluate.id.toString()
})

expect(result.isLeft()).toBe(true)
expect(result.value).toBeInstanceOf(StudentMustBeRegisteredToEvaluateError)
})
})
Loading

0 comments on commit d5865a5

Please sign in to comment.