feat: Add unit tests for backend validators and configure Jest.
This commit is contained in:
parent
ebcae0b3e7
commit
9bb941b45e
16 changed files with 2071 additions and 381 deletions
|
|
@ -0,0 +1,67 @@
|
|||
import {
|
||||
ApproveCourseValidator,
|
||||
RejectCourseValidator,
|
||||
} from '@/validators/AdminCourseApproval.validator';
|
||||
|
||||
describe('ApproveCourseValidator', () => {
|
||||
it('should pass with no body (comment optional)', () => {
|
||||
const { error } = ApproveCourseValidator.validate({});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with optional comment', () => {
|
||||
const { error } = ApproveCourseValidator.validate({
|
||||
comment: 'Looks great!',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail when comment exceeds 1000 characters', () => {
|
||||
const { error } = ApproveCourseValidator.validate({
|
||||
comment: 'a'.repeat(1001),
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/must not exceed 1000/i);
|
||||
});
|
||||
|
||||
it('should pass with comment exactly 1000 characters', () => {
|
||||
const { error } = ApproveCourseValidator.validate({
|
||||
comment: 'a'.repeat(1000),
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('RejectCourseValidator', () => {
|
||||
it('should pass with valid rejection comment', () => {
|
||||
const { error } = RejectCourseValidator.validate({
|
||||
comment: 'The content is incomplete and needs more details.',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without comment', () => {
|
||||
const { error } = RejectCourseValidator.validate({});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Comment is required when rejecting/i);
|
||||
});
|
||||
|
||||
it('should fail when comment is too short (< 10 chars)', () => {
|
||||
const { error } = RejectCourseValidator.validate({ comment: 'Too short' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/at least 10 characters/i);
|
||||
});
|
||||
|
||||
it('should pass with exactly 10 characters', () => {
|
||||
const { error } = RejectCourseValidator.validate({ comment: '1234567890' });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail when comment exceeds 1000 characters', () => {
|
||||
const { error } = RejectCourseValidator.validate({
|
||||
comment: 'a'.repeat(1001),
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/must not exceed 1000/i);
|
||||
});
|
||||
});
|
||||
263
Backend/tests/unit/validators/ChaptersLesson.validator.test.ts
Normal file
263
Backend/tests/unit/validators/ChaptersLesson.validator.test.ts
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
import {
|
||||
CreateChapterValidator,
|
||||
UpdateChapterValidator,
|
||||
ReorderChapterValidator,
|
||||
CreateLessonValidator,
|
||||
UpdateLessonValidator,
|
||||
ReorderLessonsValidator,
|
||||
AddQuestionValidator,
|
||||
UpdateQuizValidator,
|
||||
} from '@/validators/ChaptersLesson.validator';
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Chapter Validators
|
||||
// ============================================================
|
||||
|
||||
describe('CreateChapterValidator', () => {
|
||||
it('should pass with valid data', () => {
|
||||
const { error } = CreateChapterValidator.validate({
|
||||
title: { th: 'บทที่ 1', en: 'Chapter 1' },
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with optional fields', () => {
|
||||
const { error } = CreateChapterValidator.validate({
|
||||
title: { th: 'บทที่ 1', en: 'Chapter 1' },
|
||||
description: { th: 'คำอธิบาย', en: 'Description' },
|
||||
sort_order: 0,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail if title is missing', () => {
|
||||
const { error } = CreateChapterValidator.validate({});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if title.th is missing', () => {
|
||||
const { error } = CreateChapterValidator.validate({
|
||||
title: { en: 'Chapter 1' },
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if title.en is missing', () => {
|
||||
const { error } = CreateChapterValidator.validate({
|
||||
title: { th: 'บทที่ 1' },
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('UpdateChapterValidator', () => {
|
||||
it('should pass with empty object (all fields optional)', () => {
|
||||
const { error } = UpdateChapterValidator.validate({});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with partial fields', () => {
|
||||
const { error } = UpdateChapterValidator.validate({
|
||||
is_published: true,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReorderChapterValidator', () => {
|
||||
it('should pass with valid sort_order', () => {
|
||||
const { error } = ReorderChapterValidator.validate({ sort_order: 0 });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without sort_order', () => {
|
||||
const { error } = ReorderChapterValidator.validate({});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Sort order is required/i);
|
||||
});
|
||||
|
||||
it('should fail with negative sort_order', () => {
|
||||
const { error } = ReorderChapterValidator.validate({ sort_order: -1 });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Lesson Validators
|
||||
// ============================================================
|
||||
|
||||
describe('CreateLessonValidator', () => {
|
||||
it('should pass with VIDEO type', () => {
|
||||
const { error } = CreateLessonValidator.validate({
|
||||
title: { th: 'บทเรียน 1', en: 'Lesson 1' },
|
||||
type: 'VIDEO',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with QUIZ type', () => {
|
||||
const { error } = CreateLessonValidator.validate({
|
||||
title: { th: 'แบบทดสอบ', en: 'Quiz' },
|
||||
type: 'QUIZ',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail with invalid type', () => {
|
||||
const { error } = CreateLessonValidator.validate({
|
||||
title: { th: 'บทเรียน', en: 'Lesson' },
|
||||
type: 'DOCUMENT',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail without title', () => {
|
||||
const { error } = CreateLessonValidator.validate({ type: 'VIDEO' });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail without type', () => {
|
||||
const { error } = CreateLessonValidator.validate({
|
||||
title: { th: 'บทเรียน', en: 'Lesson' },
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('UpdateLessonValidator — prerequisite_lesson_ids', () => {
|
||||
it('should pass with valid array of ids', () => {
|
||||
const { error } = UpdateLessonValidator.validate({
|
||||
prerequisite_lesson_ids: [1, 2, 3],
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with null (clear prerequisites)', () => {
|
||||
const { error } = UpdateLessonValidator.validate({
|
||||
prerequisite_lesson_ids: null,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with empty array (clear prerequisites)', () => {
|
||||
const { error } = UpdateLessonValidator.validate({
|
||||
prerequisite_lesson_ids: [],
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass without the field (no change)', () => {
|
||||
const { error } = UpdateLessonValidator.validate({});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail with non-integer ids', () => {
|
||||
const { error } = UpdateLessonValidator.validate({
|
||||
prerequisite_lesson_ids: [1.5],
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail with negative ids', () => {
|
||||
const { error } = UpdateLessonValidator.validate({
|
||||
prerequisite_lesson_ids: [-1],
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail with string ids', () => {
|
||||
const { error } = UpdateLessonValidator.validate({
|
||||
prerequisite_lesson_ids: ['abc'],
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReorderLessonsValidator', () => {
|
||||
it('should pass with valid data', () => {
|
||||
const { error } = ReorderLessonsValidator.validate({
|
||||
lesson_id: 1,
|
||||
sort_order: 0,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without lesson_id', () => {
|
||||
const { error } = ReorderLessonsValidator.validate({ sort_order: 0 });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail without sort_order', () => {
|
||||
const { error } = ReorderLessonsValidator.validate({ lesson_id: 1 });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Quiz Validators
|
||||
// ============================================================
|
||||
|
||||
describe('AddQuestionValidator', () => {
|
||||
it('should pass with MULTIPLE_CHOICE type + choices', () => {
|
||||
const { error } = AddQuestionValidator.validate({
|
||||
question: { th: 'ข้อที่ 1 คืออะไร?', en: 'What is question 1?' },
|
||||
question_type: 'MULTIPLE_CHOICE',
|
||||
choices: [
|
||||
{ text: { th: 'ก', en: 'A' }, is_correct: true },
|
||||
{ text: { th: 'ข', en: 'B' }, is_correct: false },
|
||||
],
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with TRUE_FALSE type without choices', () => {
|
||||
const { error } = AddQuestionValidator.validate({
|
||||
question: { th: 'ถูกหรือผิด?', en: 'True or False?' },
|
||||
question_type: 'TRUE_FALSE',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail with invalid question_type', () => {
|
||||
const { error } = AddQuestionValidator.validate({
|
||||
question: { th: 'คำถาม', en: 'Question' },
|
||||
question_type: 'ESSAY',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail without question', () => {
|
||||
const { error } = AddQuestionValidator.validate({
|
||||
question_type: 'TRUE_FALSE',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('UpdateQuizValidator', () => {
|
||||
it('should pass with empty object (all optional)', () => {
|
||||
const { error } = UpdateQuizValidator.validate({});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with valid passing_score', () => {
|
||||
const { error } = UpdateQuizValidator.validate({ passing_score: 70 });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail with passing_score > 100', () => {
|
||||
const { error } = UpdateQuizValidator.validate({ passing_score: 101 });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail with passing_score < 0', () => {
|
||||
const { error } = UpdateQuizValidator.validate({ passing_score: -1 });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should pass with time_limit 0 (no limit)', () => {
|
||||
const { error } = UpdateQuizValidator.validate({ time_limit: 0 });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
import {
|
||||
CreateCourseValidator,
|
||||
UpdateCourseValidator,
|
||||
CloneCourseValidator,
|
||||
addInstructorCourseValidator,
|
||||
} from '@/validators/CoursesInstructor.validator';
|
||||
|
||||
// ============================================================
|
||||
// addInstructorCourseValidator
|
||||
// ============================================================
|
||||
|
||||
describe('addInstructorCourseValidator', () => {
|
||||
it('should pass with valid user_id and course_id', () => {
|
||||
const { error } = addInstructorCourseValidator.validate({
|
||||
user_id: 1,
|
||||
course_id: 2,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without user_id', () => {
|
||||
const { error } = addInstructorCourseValidator.validate({ course_id: 2 });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail without course_id', () => {
|
||||
const { error } = addInstructorCourseValidator.validate({ user_id: 1 });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// CreateCourseValidator
|
||||
// ============================================================
|
||||
|
||||
describe('CreateCourseValidator', () => {
|
||||
const validPayload = {
|
||||
category_id: 1,
|
||||
title: { th: 'คอร์สทดสอบ', en: 'Test Course' },
|
||||
slug: 'test-course',
|
||||
description: { th: 'คำอธิบาย', en: 'Description' },
|
||||
price: 500,
|
||||
is_free: false,
|
||||
have_certificate: true,
|
||||
};
|
||||
|
||||
it('should pass with all required fields', () => {
|
||||
const { error } = CreateCourseValidator.validate(validPayload);
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail if title.th is missing', () => {
|
||||
const { error } = CreateCourseValidator.validate({
|
||||
...validPayload,
|
||||
title: { en: 'Test Course' },
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if slug is missing', () => {
|
||||
const { error } = CreateCourseValidator.validate({
|
||||
...validPayload,
|
||||
slug: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if price is missing', () => {
|
||||
const { error } = CreateCourseValidator.validate({
|
||||
...validPayload,
|
||||
price: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if is_free is missing', () => {
|
||||
const { error } = CreateCourseValidator.validate({
|
||||
...validPayload,
|
||||
is_free: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should allow price = 0 (free course)', () => {
|
||||
const { error } = CreateCourseValidator.validate({
|
||||
...validPayload,
|
||||
price: 0,
|
||||
is_free: true,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// UpdateCourseValidator
|
||||
// ============================================================
|
||||
|
||||
describe('UpdateCourseValidator', () => {
|
||||
it('should pass with empty object (all optional)', () => {
|
||||
const { error } = UpdateCourseValidator.validate({});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with partial update', () => {
|
||||
const { error } = UpdateCourseValidator.validate({ price: 999 });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with title partial update (th only)', () => {
|
||||
const { error } = UpdateCourseValidator.validate({
|
||||
title: { th: 'ชื่อใหม่' },
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// CloneCourseValidator
|
||||
// ============================================================
|
||||
|
||||
describe('CloneCourseValidator', () => {
|
||||
it('should pass with valid title', () => {
|
||||
const { error } = CloneCourseValidator.validate({
|
||||
title: { th: 'คอร์ส Copy', en: 'Course Copy' },
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without title.th', () => {
|
||||
const { error } = CloneCourseValidator.validate({
|
||||
title: { en: 'Course Copy' },
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Thai title is required/i);
|
||||
});
|
||||
|
||||
it('should fail without title.en', () => {
|
||||
const { error } = CloneCourseValidator.validate({
|
||||
title: { th: 'คอร์ส Copy' },
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/English title is required/i);
|
||||
});
|
||||
|
||||
it('should fail without title entirely', () => {
|
||||
const { error } = CloneCourseValidator.validate({});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Title is required/i);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
import {
|
||||
SaveVideoProgressValidator,
|
||||
SubmitQuizValidator,
|
||||
} from '@/validators/CoursesStudent.validator';
|
||||
|
||||
describe('SaveVideoProgressValidator', () => {
|
||||
it('should pass with required field only', () => {
|
||||
const { error } = SaveVideoProgressValidator.validate({
|
||||
video_progress_seconds: 60,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with all fields', () => {
|
||||
const { error } = SaveVideoProgressValidator.validate({
|
||||
video_progress_seconds: 120,
|
||||
video_duration_seconds: 600,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with progress = 0 (start of video)', () => {
|
||||
const { error } = SaveVideoProgressValidator.validate({
|
||||
video_progress_seconds: 0,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without video_progress_seconds', () => {
|
||||
const { error } = SaveVideoProgressValidator.validate({});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Video progress seconds is required/i);
|
||||
});
|
||||
|
||||
it('should fail with negative progress', () => {
|
||||
const { error } = SaveVideoProgressValidator.validate({
|
||||
video_progress_seconds: -1,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/at least 0/i);
|
||||
});
|
||||
|
||||
it('should fail with negative video duration', () => {
|
||||
const { error } = SaveVideoProgressValidator.validate({
|
||||
video_progress_seconds: 10,
|
||||
video_duration_seconds: -1,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SubmitQuizValidator', () => {
|
||||
const validAnswer = { question_id: 1, choice_id: 2 };
|
||||
|
||||
it('should pass with valid answers', () => {
|
||||
const { error } = SubmitQuizValidator.validate({
|
||||
answers: [validAnswer, { question_id: 2, choice_id: 5 }],
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without answers', () => {
|
||||
const { error } = SubmitQuizValidator.validate({});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Answers are required/i);
|
||||
});
|
||||
|
||||
it('should fail with empty answers array', () => {
|
||||
const { error } = SubmitQuizValidator.validate({ answers: [] });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/At least one answer/i);
|
||||
});
|
||||
|
||||
it('should fail if question_id is missing in an answer', () => {
|
||||
const { error } = SubmitQuizValidator.validate({
|
||||
answers: [{ choice_id: 2 }],
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Question ID is required/i);
|
||||
});
|
||||
|
||||
it('should fail if choice_id is missing in an answer', () => {
|
||||
const { error } = SubmitQuizValidator.validate({
|
||||
answers: [{ question_id: 1 }],
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Choice ID is required/i);
|
||||
});
|
||||
|
||||
it('should fail if question_id is not a positive integer', () => {
|
||||
const { error } = SubmitQuizValidator.validate({
|
||||
answers: [{ question_id: -1, choice_id: 1 }],
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
45
Backend/tests/unit/validators/Lessons.validator.test.ts
Normal file
45
Backend/tests/unit/validators/Lessons.validator.test.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { SetYouTubeVideoValidator } from '@/validators/Lessons.validator';
|
||||
|
||||
describe('SetYouTubeVideoValidator', () => {
|
||||
it('should pass with valid youtube_video_id and video_title', () => {
|
||||
const { error } = SetYouTubeVideoValidator.validate({
|
||||
youtube_video_id: 'dQw4w9WgXcQ',
|
||||
video_title: 'Introduction to TypeScript',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without youtube_video_id', () => {
|
||||
const { error } = SetYouTubeVideoValidator.validate({
|
||||
video_title: 'Intro to TS',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/YouTube video ID is required/i);
|
||||
});
|
||||
|
||||
it('should fail with empty youtube_video_id string', () => {
|
||||
const { error } = SetYouTubeVideoValidator.validate({
|
||||
youtube_video_id: '',
|
||||
video_title: 'Intro to TS',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/cannot be empty/i);
|
||||
});
|
||||
|
||||
it('should fail without video_title', () => {
|
||||
const { error } = SetYouTubeVideoValidator.validate({
|
||||
youtube_video_id: 'dQw4w9WgXcQ',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Video title is required/i);
|
||||
});
|
||||
|
||||
it('should fail with empty video_title string', () => {
|
||||
const { error } = SetYouTubeVideoValidator.validate({
|
||||
youtube_video_id: 'dQw4w9WgXcQ',
|
||||
video_title: '',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/cannot be empty/i);
|
||||
});
|
||||
});
|
||||
115
Backend/tests/unit/validators/announcements.validator.test.ts
Normal file
115
Backend/tests/unit/validators/announcements.validator.test.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import {
|
||||
CreateAnnouncementValidator,
|
||||
UpdateAnnouncementValidator,
|
||||
} from '@/validators/announcements.validator';
|
||||
|
||||
describe('CreateAnnouncementValidator', () => {
|
||||
const validPayload = {
|
||||
title: { th: 'ประกาศใหม่', en: 'New Announcement' },
|
||||
content: { th: 'เนื้อหา', en: 'Content' },
|
||||
status: 'DRAFT',
|
||||
is_pinned: false,
|
||||
};
|
||||
|
||||
it('should pass with all required fields', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate(validPayload);
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with optional published_at as ISO date', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate({
|
||||
...validPayload,
|
||||
published_at: '2026-03-01T00:00:00.000Z',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail with invalid published_at format', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate({
|
||||
...validPayload,
|
||||
published_at: '01-03-2026',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/ISO date/i);
|
||||
});
|
||||
|
||||
it('should fail with invalid status', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate({
|
||||
...validPayload,
|
||||
status: 'HIDDEN',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/DRAFT, PUBLISHED, ARCHIVED/i);
|
||||
});
|
||||
|
||||
it('should pass with PUBLISHED status', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate({
|
||||
...validPayload,
|
||||
status: 'PUBLISHED',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with ARCHIVED status', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate({
|
||||
...validPayload,
|
||||
status: 'ARCHIVED',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without title', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate({
|
||||
...validPayload,
|
||||
title: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Title is required/i);
|
||||
});
|
||||
|
||||
it('should fail without content', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate({
|
||||
...validPayload,
|
||||
content: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Content is required/i);
|
||||
});
|
||||
|
||||
it('should fail without is_pinned', () => {
|
||||
const { error } = CreateAnnouncementValidator.validate({
|
||||
...validPayload,
|
||||
is_pinned: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/is_pinned is required/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UpdateAnnouncementValidator', () => {
|
||||
it('should pass with empty object (all optional)', () => {
|
||||
const { error } = UpdateAnnouncementValidator.validate({});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with partial update', () => {
|
||||
const { error } = UpdateAnnouncementValidator.validate({
|
||||
status: 'PUBLISHED',
|
||||
is_pinned: true,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail with invalid status', () => {
|
||||
const { error } = UpdateAnnouncementValidator.validate({ status: 'DELETED' });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail with invalid published_at format', () => {
|
||||
const { error } = UpdateAnnouncementValidator.validate({
|
||||
published_at: 'not-a-date',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/ISO date/i);
|
||||
});
|
||||
});
|
||||
246
Backend/tests/unit/validators/auth.validator.test.ts
Normal file
246
Backend/tests/unit/validators/auth.validator.test.ts
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import {
|
||||
loginSchema,
|
||||
registerSchema,
|
||||
refreshTokenSchema,
|
||||
resetPasswordSchema,
|
||||
changePasswordSchema,
|
||||
resetRequestSchema,
|
||||
} from '@/validators/auth.validator';
|
||||
|
||||
// ============================================================
|
||||
// loginSchema
|
||||
// ============================================================
|
||||
|
||||
describe('loginSchema', () => {
|
||||
it('should pass with valid email and password', () => {
|
||||
const { error } = loginSchema.validate({
|
||||
email: 'user@example.com',
|
||||
password: 'password123',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail with invalid email format', () => {
|
||||
const { error } = loginSchema.validate({
|
||||
email: 'not-an-email',
|
||||
password: 'password123',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/valid email/i);
|
||||
});
|
||||
|
||||
it('should fail without email', () => {
|
||||
const { error } = loginSchema.validate({ password: 'password123' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Email is required/i);
|
||||
});
|
||||
|
||||
it('should fail without password', () => {
|
||||
const { error } = loginSchema.validate({ email: 'user@example.com' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Password is required/i);
|
||||
});
|
||||
|
||||
it('should fail with password shorter than 6 characters', () => {
|
||||
const { error } = loginSchema.validate({
|
||||
email: 'user@example.com',
|
||||
password: '12345',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/at least 6 characters/i);
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// registerSchema
|
||||
// ============================================================
|
||||
|
||||
describe('registerSchema', () => {
|
||||
const validPayload = {
|
||||
username: 'john_doe',
|
||||
email: 'john@example.com',
|
||||
password: 'securepass',
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
phone: '0812345678',
|
||||
};
|
||||
|
||||
it('should pass with all required fields', () => {
|
||||
const { error } = registerSchema.validate(validPayload);
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with optional prefix', () => {
|
||||
const { error } = registerSchema.validate({
|
||||
...validPayload,
|
||||
prefix: { th: 'นาย', en: 'Mr.' },
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail if username has invalid characters', () => {
|
||||
const { error } = registerSchema.validate({
|
||||
...validPayload,
|
||||
username: 'john doe!',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/letters, numbers, and underscores/i);
|
||||
});
|
||||
|
||||
it('should fail if username is too short (< 3 chars)', () => {
|
||||
const { error } = registerSchema.validate({
|
||||
...validPayload,
|
||||
username: 'ab',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/at least 3 characters/i);
|
||||
});
|
||||
|
||||
it('should fail if username is too long (> 50 chars)', () => {
|
||||
const { error } = registerSchema.validate({
|
||||
...validPayload,
|
||||
username: 'a'.repeat(51),
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/not exceed 50 characters/i);
|
||||
});
|
||||
|
||||
it('should fail with invalid email', () => {
|
||||
const { error } = registerSchema.validate({
|
||||
...validPayload,
|
||||
email: 'bad-email',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if phone is too short (< 10 chars)', () => {
|
||||
const { error } = registerSchema.validate({
|
||||
...validPayload,
|
||||
phone: '081234',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail without first_name', () => {
|
||||
const { error } = registerSchema.validate({
|
||||
...validPayload,
|
||||
first_name: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/First name is required/i);
|
||||
});
|
||||
|
||||
it('should fail without last_name', () => {
|
||||
const { error } = registerSchema.validate({
|
||||
...validPayload,
|
||||
last_name: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Last name is required/i);
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// refreshTokenSchema
|
||||
// ============================================================
|
||||
|
||||
describe('refreshTokenSchema', () => {
|
||||
it('should pass with a valid refreshToken', () => {
|
||||
const { error } = refreshTokenSchema.validate({
|
||||
refreshToken: 'some-refresh-token-string',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without refreshToken', () => {
|
||||
const { error } = refreshTokenSchema.validate({});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Refresh token is required/i);
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// resetPasswordSchema
|
||||
// ============================================================
|
||||
|
||||
describe('resetPasswordSchema', () => {
|
||||
it('should pass with valid token and password', () => {
|
||||
const { error } = resetPasswordSchema.validate({
|
||||
token: 'reset-token-abc',
|
||||
password: 'newpassword',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without token', () => {
|
||||
const { error } = resetPasswordSchema.validate({ password: 'newpassword' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Reset token is required/i);
|
||||
});
|
||||
|
||||
it('should fail with password too short', () => {
|
||||
const { error } = resetPasswordSchema.validate({
|
||||
token: 'abc',
|
||||
password: '12345',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/at least 6 characters/i);
|
||||
});
|
||||
|
||||
it('should fail with password too long (> 100 chars)', () => {
|
||||
const { error } = resetPasswordSchema.validate({
|
||||
token: 'abc',
|
||||
password: 'a'.repeat(101),
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// changePasswordSchema (auth)
|
||||
// ============================================================
|
||||
|
||||
describe('changePasswordSchema (auth.validator)', () => {
|
||||
it('should pass with valid old and new passwords', () => {
|
||||
const { error } = changePasswordSchema.validate({
|
||||
oldPassword: 'oldpass123',
|
||||
newPassword: 'newpass456',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without oldPassword', () => {
|
||||
const { error } = changePasswordSchema.validate({ newPassword: 'newpass456' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Password is required/i);
|
||||
});
|
||||
|
||||
it('should fail without newPassword', () => {
|
||||
const { error } = changePasswordSchema.validate({ oldPassword: 'oldpass123' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Password is required/i);
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// resetRequestSchema
|
||||
// ============================================================
|
||||
|
||||
describe('resetRequestSchema', () => {
|
||||
it('should pass with valid email', () => {
|
||||
const { error } = resetRequestSchema.validate({ email: 'user@example.com' });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without email', () => {
|
||||
const { error } = resetRequestSchema.validate({});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Email is required/i);
|
||||
});
|
||||
|
||||
it('should fail with invalid email', () => {
|
||||
const { error } = resetRequestSchema.validate({ email: 'not-email' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/valid email/i);
|
||||
});
|
||||
});
|
||||
121
Backend/tests/unit/validators/categories.validator.test.ts
Normal file
121
Backend/tests/unit/validators/categories.validator.test.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import {
|
||||
CreateCategoryValidator,
|
||||
UpdateCategoryValidator,
|
||||
} from '@/validators/categories.validator';
|
||||
|
||||
describe('CreateCategoryValidator', () => {
|
||||
const validPayload = {
|
||||
name: { th: 'การพัฒนาเว็บ', en: 'Web Development' },
|
||||
slug: 'web-development',
|
||||
description: { th: 'หมวดหมู่การพัฒนาเว็บ', en: 'Web development category' },
|
||||
};
|
||||
|
||||
it('should pass with all required fields', () => {
|
||||
const { error } = CreateCategoryValidator.validate(validPayload);
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with optional created_by', () => {
|
||||
const { error } = CreateCategoryValidator.validate({
|
||||
...validPayload,
|
||||
created_by: 1,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without name', () => {
|
||||
const { error } = CreateCategoryValidator.validate({
|
||||
...validPayload,
|
||||
name: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Name is required/i);
|
||||
});
|
||||
|
||||
it('should fail without name.th', () => {
|
||||
const { error } = CreateCategoryValidator.validate({
|
||||
...validPayload,
|
||||
name: { en: 'Web Development' },
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Thai name is required/i);
|
||||
});
|
||||
|
||||
it('should fail without slug', () => {
|
||||
const { error } = CreateCategoryValidator.validate({
|
||||
...validPayload,
|
||||
slug: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Slug is required/i);
|
||||
});
|
||||
|
||||
it('should fail with invalid slug format (uppercase)', () => {
|
||||
const { error } = CreateCategoryValidator.validate({
|
||||
...validPayload,
|
||||
slug: 'Web-Development',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/lowercase with hyphens/i);
|
||||
});
|
||||
|
||||
it('should fail with invalid slug format (spaces)', () => {
|
||||
const { error } = CreateCategoryValidator.validate({
|
||||
...validPayload,
|
||||
slug: 'web development',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should pass with valid slug formats', () => {
|
||||
const slugs = ['web', 'web-dev', 'web-development-101'];
|
||||
for (const slug of slugs) {
|
||||
const { error } = CreateCategoryValidator.validate({
|
||||
...validPayload,
|
||||
slug,
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail without description', () => {
|
||||
const { error } = CreateCategoryValidator.validate({
|
||||
...validPayload,
|
||||
description: undefined,
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Description is required/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UpdateCategoryValidator', () => {
|
||||
it('should pass with only required id', () => {
|
||||
const { error } = UpdateCategoryValidator.validate({ id: 1 });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with all optional fields', () => {
|
||||
const { error } = UpdateCategoryValidator.validate({
|
||||
id: 1,
|
||||
name: { th: 'ใหม่', en: 'New' },
|
||||
slug: 'new-category',
|
||||
description: { th: 'คำอธิบาย', en: 'Description' },
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without id', () => {
|
||||
const { error } = UpdateCategoryValidator.validate({ name: { th: 'ใหม่', en: 'New' } });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Category ID is required/i);
|
||||
});
|
||||
|
||||
it('should fail with invalid slug format', () => {
|
||||
const { error } = UpdateCategoryValidator.validate({
|
||||
id: 1,
|
||||
slug: 'Invalid Slug!',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/lowercase with hyphens/i);
|
||||
});
|
||||
});
|
||||
100
Backend/tests/unit/validators/user.validator.test.ts
Normal file
100
Backend/tests/unit/validators/user.validator.test.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import {
|
||||
profileUpdateSchema,
|
||||
changePasswordSchema,
|
||||
} from '@/validators/user.validator';
|
||||
|
||||
describe('profileUpdateSchema', () => {
|
||||
it('should pass with empty object (all optional)', () => {
|
||||
const { error } = profileUpdateSchema.validate({});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with all fields', () => {
|
||||
const { error } = profileUpdateSchema.validate({
|
||||
prefix: { th: 'นาย', en: 'Mr.' },
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
phone: '0812345678',
|
||||
avatar_url: 'https://example.com/avatar.jpg',
|
||||
birth_date: new Date('1990-01-01'),
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should pass with partial update (first_name only)', () => {
|
||||
const { error } = profileUpdateSchema.validate({ first_name: 'Jane' });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail if first_name is empty string', () => {
|
||||
const { error } = profileUpdateSchema.validate({ first_name: '' });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if first_name exceeds 100 characters', () => {
|
||||
const { error } = profileUpdateSchema.validate({ first_name: 'a'.repeat(101) });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if phone is too short (< 10 chars)', () => {
|
||||
const { error } = profileUpdateSchema.validate({ phone: '081234' });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if phone exceeds 15 characters', () => {
|
||||
const { error } = profileUpdateSchema.validate({ phone: '1'.repeat(16) });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should pass with valid birth_date', () => {
|
||||
const { error } = profileUpdateSchema.validate({ birth_date: new Date('2000-06-15') });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('changePasswordSchema (user.validator)', () => {
|
||||
it('should pass with valid old and new passwords', () => {
|
||||
const { error } = changePasswordSchema.validate({
|
||||
oldPassword: 'oldpass123',
|
||||
newPassword: 'newpass456',
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without oldPassword', () => {
|
||||
const { error } = changePasswordSchema.validate({ newPassword: 'newpass456' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Old password is required/i);
|
||||
});
|
||||
|
||||
it('should fail without newPassword', () => {
|
||||
const { error } = changePasswordSchema.validate({ oldPassword: 'oldpass123' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/New password is required/i);
|
||||
});
|
||||
|
||||
it('should fail if oldPassword is shorter than 6 chars', () => {
|
||||
const { error } = changePasswordSchema.validate({
|
||||
oldPassword: '12345',
|
||||
newPassword: 'newpass456',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/at least 6 characters/i);
|
||||
});
|
||||
|
||||
it('should fail if newPassword is shorter than 6 chars', () => {
|
||||
const { error } = changePasswordSchema.validate({
|
||||
oldPassword: 'oldpass123',
|
||||
newPassword: '123',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if oldPassword exceeds 100 characters', () => {
|
||||
const { error } = changePasswordSchema.validate({
|
||||
oldPassword: 'a'.repeat(101),
|
||||
newPassword: 'newpass456',
|
||||
});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import {
|
||||
getUserByIdValidator,
|
||||
updateUserRoleValidator,
|
||||
} from '@/validators/usermanagement.validator';
|
||||
|
||||
describe('getUserByIdValidator', () => {
|
||||
it('should pass with valid id', () => {
|
||||
const { error } = getUserByIdValidator.validate({ id: 1 });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without id', () => {
|
||||
const { error } = getUserByIdValidator.validate({});
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail with non-numeric id', () => {
|
||||
const { error } = getUserByIdValidator.validate({ id: 'abc' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/ID must be a number/i);
|
||||
});
|
||||
|
||||
it('should pass with id = 0', () => {
|
||||
// Joi number() allows 0 by default unless positive() is specified
|
||||
const { error } = getUserByIdValidator.validate({ id: 0 });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateUserRoleValidator', () => {
|
||||
it('should pass with valid id and role_id', () => {
|
||||
const { error } = updateUserRoleValidator.validate({ id: 1, role_id: 2 });
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fail without id', () => {
|
||||
const { error } = updateUserRoleValidator.validate({ role_id: 2 });
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail without role_id', () => {
|
||||
const { error } = updateUserRoleValidator.validate({ id: 1 });
|
||||
expect(error).toBeDefined();
|
||||
// Joi uses field name in message when custom messages don't match
|
||||
expect(error?.details[0].message).toContain('role_id');
|
||||
});
|
||||
|
||||
it('should fail with non-numeric role_id', () => {
|
||||
const { error } = updateUserRoleValidator.validate({ id: 1, role_id: 'admin' });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/Role ID must be a number/i);
|
||||
});
|
||||
|
||||
it('should fail with non-numeric id', () => {
|
||||
const { error } = updateUserRoleValidator.validate({ id: 'abc', role_id: 1 });
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.details[0].message).toMatch(/ID must be a number/i);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue