diff --git a/backend/tests/service/learning-objects.test.ts b/backend/tests/service/learning-objects.test.ts new file mode 100644 index 00000000..130c237e --- /dev/null +++ b/backend/tests/service/learning-objects.test.ts @@ -0,0 +1,117 @@ +import { describe, it, expect, vi } from 'vitest'; +import { LearningObjectMetadata, LearningPath } from '../../src/interfaces/learningPath'; +import { fetchWithLogging } from '../../src/util/apiHelper'; +import { getLearningObjectById, getLearningObjectsFromPath } from '../../src/services/learningObjects'; +import { fetchLearningPaths } from '../../src/services/learningPaths'; + +// Mock API functions +vi.mock('../../src/util/apiHelper', () => ({ + fetchWithLogging: vi.fn(), +})); + +vi.mock('../../src/services/learningPaths', () => ({ + fetchLearningPaths: vi.fn(), +})); + +describe('getLearningObjectById', () => { + const hruid = 'test-object'; + const language = 'en'; + const mockMetadata: LearningObjectMetadata = { + hruid, + _id: '123', + uuid: 'uuid-123', + version: 1, + title: 'Test Object', + language, + difficulty: 5, + estimated_time: 120, + available: true, + teacher_exclusive: false, + educational_goals: [{ source: 'source', id: 'id' }], + keywords: ['robotics'], + description: 'A test object', + target_ages: [10, 12], + content_type: 'markdown', + content_location: '', + }; + + it('✅ Should return a filtered learning object when API provides data', async () => { + vi.mocked(fetchWithLogging).mockResolvedValueOnce(mockMetadata); + + const result = await getLearningObjectById(hruid, language); + + expect(result).toEqual({ + key: hruid, + _id: '123', + uuid: 'uuid-123', + version: 1, + title: 'Test Object', + htmlUrl: expect.stringContaining('/learningObject/getRaw?hruid=test-object&language=en'), + language, + difficulty: 5, + estimatedTime: 120, + available: true, + teacherExclusive: false, + educationalGoals: [{ source: 'source', id: 'id' }], + keywords: ['robotics'], + description: 'A test object', + targetAges: [10, 12], + contentType: 'markdown', + contentLocation: '', + }); + }); + + it('⚠️ Should return null if API returns no metadata', async () => { + vi.mocked(fetchWithLogging).mockResolvedValueOnce(null); + const result = await getLearningObjectById(hruid, language); + expect(result).toBeNull(); + }); +}); + +describe('getLearningObjectsFromPath', () => { + const hruid = 'test-path'; + const language = 'en'; + + it('✅ Should not give error or warning', async () => { + const mockPathResponse: LearningPath[] = [ + { + _id: 'path-1', + hruid, + language, + title: 'Test Path', + description: '', + num_nodes: 1, + num_nodes_left: 0, + nodes: [], + keywords: '', + target_ages: [], + min_age: 10, + max_age: 12, + __order: 1, + }, + ]; + + vi.mocked(fetchLearningPaths).mockResolvedValueOnce({ + success: true, + source: 'Test Source', + data: mockPathResponse, + }); + + const result = await getLearningObjectsFromPath(hruid, language); + expect(result).toEqual([]); + }); + + it('⚠️ Should give a warning', async () => { + vi.mocked(fetchLearningPaths).mockResolvedValueOnce({ success: false, source: 'Test Source', data: [] }); + + const result = await getLearningObjectsFromPath(hruid, language); + expect(result).toEqual([]); + }); + + it('❌ Should give an error', async () => { + vi.mocked(fetchLearningPaths).mockRejectedValueOnce(new Error('API Error')); + + const result = await getLearningObjectsFromPath(hruid, language); + expect(result).toEqual([]); + }); +}); diff --git a/backend/tests/service/learning-paths.test.ts b/backend/tests/service/learning-paths.test.ts new file mode 100644 index 00000000..c002dbac --- /dev/null +++ b/backend/tests/service/learning-paths.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, vi } from 'vitest'; +import { fetchLearningPaths, searchLearningPaths } from '../../src/services/learningPaths'; +import { fetchWithLogging } from '../../src/util/apiHelper'; +import { LearningPathResponse } from '../../src/interfaces/learningPath'; + +// Mock the fetchWithLogging module using vi +vi.mock('../../src/util/apiHelper', () => ({ + fetchWithLogging: vi.fn(), +})); + +describe('fetchLearningPaths', () => { + // Mock data and response + const mockHruids = ['pn_werking', 'art1']; + const language = 'en'; + const source = 'Test Source'; + const mockResponse = [{ title: 'Test Path', hruids: mockHruids }]; + + it('✅ Should return a successful response when HRUIDs are provided', async () => { + // Mock the function to return mockResponse + vi.mocked(fetchWithLogging).mockResolvedValue(mockResponse); + + const result: LearningPathResponse = await fetchLearningPaths(mockHruids, language, source); + + expect(result.success).toBe(true); + expect(result.data).toEqual(mockResponse); + expect(result.source).toBe(source); + }); + + it('⚠️ Should return an error when no HRUIDs are provided', async () => { + vi.mocked(fetchWithLogging).mockResolvedValue(mockResponse); + + const result: LearningPathResponse = await fetchLearningPaths([], language, source); + + expect(result.success).toBe(false); + expect(result.data).toBeNull(); + expect(result.message).toBe(`No HRUIDs provided for ${source}.`); + }); + + it('⚠️ Should return a failure response when no learning paths are found', async () => { + // Mock fetchWithLogging to return an empty array + vi.mocked(fetchWithLogging).mockResolvedValue([]); + + const result: LearningPathResponse = await fetchLearningPaths(mockHruids, language, source); + + expect(result.success).toBe(false); + expect(result.data).toEqual([]); + expect(result.message).toBe(`No learning paths found for ${source}.`); + }); +}); + +describe('searchLearningPaths', () => { + const query = + 'https://dwengo.org/backend/api/learningPath/getPathsFromIdList?pathIdList=%7B%22hruids%22:%5B%22pn_werking%22,%22un_artificiele_intelligentie%22%5D%7D&language=nl'; + const language = 'nl'; + + it('✅ Should return search results when API responds with data', async () => { + const mockResults = [ + { + _id: '67b4488c9dadb305c4104618', + language: 'nl', + hruid: 'pn_werking', + title: 'Werken met notebooks', + description: 'Een korte inleiding tot Python notebooks. Hoe ga je gemakkelijk en efficiënt met de notebooks aan de slag?', + num_nodes: 0, + num_nodes_left: 0, + nodes: [], + keywords: 'Python KIKS Wiskunde STEM AI', + target_ages: [14, 15, 16, 17, 18], + min_age: 14, + max_age: 18, + __order: 0, + }, + ]; + + // Mock fetchWithLogging to return search results + vi.mocked(fetchWithLogging).mockResolvedValue(mockResults); + + const result = await searchLearningPaths(query, language); + + expect(result).toEqual(mockResults); + }); + + it('⚠️ Should return an empty array when API returns no results', async () => { + vi.mocked(fetchWithLogging).mockResolvedValue([]); + + const result = await searchLearningPaths(query, language); + + expect(result).toEqual([]); + }); +});