Skip to content
This repository was archived by the owner on Feb 11, 2026. It is now read-only.
Merged

Test #49

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions test/ai.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import request from 'supertest';

import { AiController } from '@/presentation/controllers/ai.controller';
import { IAiService } from '@/application/ports/ai.port';
import { SessionGuard } from '@/presentation/guards/session.guard';
import {
PredictionDto,
PredictionResponseDto,
} from '@/presentation/dtos/ai.dto';
Comment on lines +8 to +11
import {
AiService,
PredictionWithModules,
} from '@/application/services/ai.service';
Comment on lines +12 to +15

// ============================
// Mock SessionGuard
// ============================
jest.mock('@/presentation/guards/session.guard', () => ({
SessionGuard: jest.fn().mockImplementation(() => ({
canActivate: () => true,
})),
}));

describe('AiController (Integration)', () => {
let app: INestApplication;
let aiService: jest.Mocked<IAiService>;

const mockSession = {
user: { id: 'user-1', role: 'STUDENT' },
};

const predictionDto: PredictionDto = {
currentStudy: 'CS',
interests: ['AI'],
wantedStudyCreditRange: [5, 10],
locationPreference: ['Amsterdam'],
learningGoals: ['ML'],
levelPreference: ['HBO'],
preferredLanguage: 'EN',
preferredPeriod: ['P2'],
};

const mockPrediction: PredictionWithModules = {
predictions: [
{
module: {
id: 1,
name: 'AI Basics',
shortDescription: 'Intro',
studyCredits: 5,
level: 'HBO',
location: [{ id: 1, name: 'Breda' }],
startDate: '2025-02-01',
},
score: 0.9,
motivation: 'Perfect match',
},
],
};

beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AiController],
providers: [
{
provide: 'SERVICE.AI',
useValue: {
getPrediction: jest.fn(),
},
},
],
})
.overrideGuard(SessionGuard)
.useValue({ canActivate: () => true })
.compile();

app = module.createNestApplication();

// Fake session middleware
app.use((req: any, _res: any, next: any) => {
req.session = mockSession;
next();
});

await app.init();

aiService = module.get('SERVICE.AI');
});

afterAll(async () => {
await app.close();
});

// ============================
// POST /ai/predict → Success
// ============================
it('POST /ai/predict → retourneert voorspellingen', async () => {
aiService.getPrediction.mockResolvedValue({
_tag: 'Success',
data: mockPrediction,
});

const response = await request(app.getHttpServer())
.post('/ai/predict')
.send(predictionDto)
.expect(200);

expect(response.body.predictions).toBeDefined();
expect(response.body.predictions).toHaveLength(1);
expect(response.body.predictions[0].module.name).toBe('AI Basics');
});

// ============================
// POST /ai/predict → Failure
// ============================
it('POST /ai/predict → faalt bij error', async () => {
aiService.getPrediction.mockResolvedValue({
_tag: 'Failure',
error: new Error('AI service unavailable'),
});

const response = await request(app.getHttpServer())
.post('/ai/predict')
.send(predictionDto)
.expect(400);

expect(response.body.message).toBe('Prediction generation failed');
expect(response.body.details).toBe('AI service unavailable');
});

// ============================
// POST /ai/predict → lege predictions
// ============================
it('POST /ai/predict → retourneert lege array', async () => {
aiService.getPrediction.mockResolvedValue({
_tag: 'Success',
data: { predictions: [] },
});

const response = await request(app.getHttpServer())
.post('/ai/predict')
.send(predictionDto)
.expect(200);

expect(response.body.predictions).toBeDefined();
expect(response.body.predictions).toHaveLength(0);
});
});
125 changes: 125 additions & 0 deletions test/ai.intergration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AiController } from '@/presentation/controllers/ai.controller';
import { AiService } from '@/application/services/ai.service';
import { BadRequestException } from '@nestjs/common';
import { PredictionDto } from '@/presentation/dtos/ai.dto';
import { LoggerService } from '@/logger.service';
import { SessionGuard } from '@/presentation/guards/session.guard';

describe('AI Integration Test', () => {
let aiController: AiController;
let aiService: AiService;

const mockLogger = {
setContext: jest.fn(),
log: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};

const mockPredictionClient = {
getPrediction: jest.fn(),
};

const mockModuleService = {
findManyByIds: jest.fn(),
};

const mockPredictionResult = [
{ id: 1, similarity_score: 0.95, motivation: 'Great match' },
{ id: 2, similarity_score: 0.87, motivation: 'Good fit' },
];

const mockModules = [
{
id: 1,
name: 'Math 101',
shortDescription: 'Basic Math',
studyCredits: 30,
level: 'NFQL5',
location: ['Breda', 'Roosendaal'],
startDate: new Date(),
},
{
id: 2,
name: 'Physics 101',
shortDescription: 'Basic Physics',
studyCredits: 15,
level: 'Beginner',
location: ['Breda', 'Roosendaal'],
startDate: new Date(),
},
];

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AiController],
providers: [
{ provide: 'SERVICE.AI', useClass: AiService },
{ provide: 'CLIENT.PREDICTION', useValue: mockPredictionClient },
{ provide: 'SERVICE.MODULE', useValue: mockModuleService },
{ provide: LoggerService, useValue: mockLogger },
],
})
.overrideGuard(SessionGuard) // <-- guard overschrijven
.useValue({ canActivate: () => true })
.compile();

aiController = module.get<AiController>(AiController);
aiService = module.get<AiService>('SERVICE.AI');
});

afterEach(() => {
jest.clearAllMocks();
});

it('should return predictions successfully', async () => {
mockPredictionClient.getPrediction.mockResolvedValue(mockPredictionResult);
mockModuleService.findManyByIds.mockResolvedValue(mockModules);

const session: any = { user: { id: 'user-1', role: 'STUDENT' } };
const wantedCredits: [number, number] = [15, 30];

const dto: PredictionDto = {
currentStudy: 'Science',
interests: ['Math', 'Physics'],
wantedStudyCreditRange: wantedCredits,
locationPreference: ['Breda', 'Roosendaal'],
learningGoals: ['Understand basics'],
levelPreference: ['NFQL5'],
preferredLanguage: 'EN',
preferredPeriod: ['P1', 'P2'],
};

const result = await aiController.createPrediction(session, dto);

expect(result.predictions.length).toBe(2);
expect(result.predictions[0].module.name).toBe('Math 101');

expect(mockPredictionClient.getPrediction).toHaveBeenCalled();
expect(mockModuleService.findManyByIds).toHaveBeenCalledWith([1, 2]);
});

it('should throw BadRequestException on failure', async () => {
mockPredictionClient.getPrediction.mockRejectedValue(
new Error('AI service down'),
);
const session: any = { user: { id: 'user-1', role: 'STUDENT' } };
const wantedCredits: [number, number] = [15, 30];

const dto: PredictionDto = {
currentStudy: 'Science',
interests: [],
wantedStudyCreditRange: wantedCredits,
locationPreference: ['Breda', 'Roosendaal'],
learningGoals: [],
levelPreference: ['NFQL5'],
preferredLanguage: 'EN',
preferredPeriod: ['P1', 'P2'],
};

await expect(aiController.createPrediction(session, dto)).rejects.toThrow(
BadRequestException,
);
});
});
Loading
Loading