Skip to content
Merged
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

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion src/modules/admin/admin-filters.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,21 @@ describe('AdminFiltersController', () => {
});

it('should delegate program listing to the filters service', async () => {
const programs = [{ id: 'p-1', code: 'BSCS', name: 'Computer Science' }];
const programs = [
{
id: 'p-1',
code: 'BSCS',
name: 'Computer Science',
moodleCategoryId: 42,
},
];
filtersService.GetPrograms.mockResolvedValue(programs);

const result = await controller.GetPrograms({ departmentId: 'd-1' });

expect(filtersService.GetPrograms).toHaveBeenCalledWith('d-1');
expect(result).toEqual(programs);
expect(result[0].moodleCategoryId).toBe(42);
});

it('should pass undefined departmentId when not provided', async () => {
Expand Down
5 changes: 3 additions & 2 deletions src/modules/admin/admin-filters.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { FilterOptionResponseDto } from './dto/responses/filter-option.response.
import { FilterFacultyResponseDto } from './dto/responses/filter-faculty.response.dto';
import { FilterCourseResponseDto } from './dto/responses/filter-course.response.dto';
import { FilterVersionResponseDto } from './dto/responses/filter-version.response.dto';
import { ProgramFilterOptionResponseDto } from './dto/responses/program-filter-option.response.dto';
import { SemesterFilterResponseDto } from './dto/responses/semester-filter.response.dto';

@ApiTags('Admin')
Expand Down Expand Up @@ -72,10 +73,10 @@ export class AdminFiltersController {
type: String,
description: 'Filter by department UUID',
})
@ApiResponse({ status: 200, type: [FilterOptionResponseDto] })
@ApiResponse({ status: 200, type: [ProgramFilterOptionResponseDto] })
async GetPrograms(
@Query() query: FilterProgramsQueryDto,
): Promise<FilterOptionResponseDto[]> {
): Promise<ProgramFilterOptionResponseDto[]> {
return this.filtersService.GetPrograms(query.departmentId);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

export class ProgramFilterOptionResponseDto {
@ApiProperty()
id: string;

@ApiProperty()
code: string;

@ApiPropertyOptional({ nullable: true })
name: string | null;

@ApiProperty({ description: 'Moodle category ID for this program' })
moodleCategoryId: number;

static MapProgram(entity: {
id: string;
code: string;
name?: string;
moodleCategoryId: number;
}): ProgramFilterOptionResponseDto {
const dto = new ProgramFilterOptionResponseDto();
dto.id = entity.id;
dto.code = entity.code;
dto.name = entity.name ?? null;
dto.moodleCategoryId = entity.moodleCategoryId;
return dto;
}
}
70 changes: 70 additions & 0 deletions src/modules/admin/services/admin-filters.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { EntityManager } from '@mikro-orm/postgresql';
import { Test, TestingModule } from '@nestjs/testing';
import { Program } from 'src/entities/program.entity';
import { AdminFiltersService } from './admin-filters.service';
import { ProgramFilterOptionResponseDto } from '../dto/responses/program-filter-option.response.dto';

describe('AdminFiltersService', () => {
let service: AdminFiltersService;
let em: { find: jest.Mock };

beforeEach(async () => {
em = {
find: jest.fn().mockResolvedValue([]),
};

const module: TestingModule = await Test.createTestingModule({
providers: [
AdminFiltersService,
{ provide: EntityManager, useValue: em },
],
}).compile();

service = module.get(AdminFiltersService);
});

describe('GetPrograms', () => {
it('should map moodleCategoryId via ProgramFilterOptionResponseDto', async () => {
const programEntity = {
id: 'p-1',
code: 'BSCS',
name: 'Computer Science',
moodleCategoryId: 42,
};
em.find.mockResolvedValue([programEntity]);

const result = await service.GetPrograms('d-1');

expect(em.find).toHaveBeenCalledWith(
Program,
{ department: 'd-1' },
{ orderBy: { code: 'ASC' } },
);
expect(result).toHaveLength(1);
expect(result[0].moodleCategoryId).toBe(42);
expect(result[0].id).toBe('p-1');
expect(result[0].code).toBe('BSCS');
expect(result[0].name).toBe('Computer Science');
expect(result[0]).toBeInstanceOf(ProgramFilterOptionResponseDto);
});

it('should map name to null when entity name is undefined', async () => {
const programEntity = {
id: 'p-2',
code: 'BSIT',
moodleCategoryId: 55,
};
em.find.mockResolvedValue([programEntity]);

const result = await service.GetPrograms();

expect(em.find).toHaveBeenCalledWith(
Program,
{},
{ orderBy: { code: 'ASC' } },
);
expect(result[0].name).toBeNull();
expect(result[0].moodleCategoryId).toBe(55);
});
});
});
7 changes: 5 additions & 2 deletions src/modules/admin/services/admin-filters.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { FilterOptionResponseDto } from '../dto/responses/filter-option.response
import { FilterFacultyResponseDto } from '../dto/responses/filter-faculty.response.dto';
import { FilterCourseResponseDto } from '../dto/responses/filter-course.response.dto';
import { FilterVersionResponseDto } from '../dto/responses/filter-version.response.dto';
import { ProgramFilterOptionResponseDto } from '../dto/responses/program-filter-option.response.dto';
import { SemesterFilterResponseDto } from '../dto/responses/semester-filter.response.dto';

@Injectable()
Expand Down Expand Up @@ -94,15 +95,17 @@ export class AdminFiltersService {
return departments.map((d) => FilterOptionResponseDto.Map(d));
}

async GetPrograms(departmentId?: string): Promise<FilterOptionResponseDto[]> {
async GetPrograms(
departmentId?: string,
): Promise<ProgramFilterOptionResponseDto[]> {
const filter: FilterQuery<Program> = {};
if (departmentId) {
filter.department = departmentId;
}
const programs = await this.em.find(Program, filter, {
orderBy: { code: 'ASC' },
});
return programs.map((p) => FilterOptionResponseDto.Map(p));
return programs.map((p) => ProgramFilterOptionResponseDto.MapProgram(p));
}

GetRoles(): UserRole[] {
Expand Down
Loading