251209:1453 Frontend: progress nest = UAT & Bug Fixing
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { Contract } from './contract.entity';
|
||||
|
||||
@Entity('projects')
|
||||
export class Project extends BaseEntity {
|
||||
@@ -14,4 +15,7 @@ export class Project extends BaseEntity {
|
||||
|
||||
@Column({ name: 'is_active', default: 1, type: 'tinyint' })
|
||||
isActive!: boolean;
|
||||
|
||||
@OneToMany(() => Contract, (contract) => contract.project)
|
||||
contracts!: Contract[];
|
||||
}
|
||||
|
||||
@@ -1,19 +1,67 @@
|
||||
import { Controller, Get, UseGuards } from '@nestjs/common';
|
||||
import { ProjectService } from './project.service.js';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard.js';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ProjectController } from './project.controller';
|
||||
import { ProjectService } from './project.service';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { RbacGuard } from '../../common/guards/rbac.guard';
|
||||
|
||||
@Controller('projects')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class ProjectController {
|
||||
constructor(private readonly projectService: ProjectService) {}
|
||||
describe('ProjectController', () => {
|
||||
let controller: ProjectController;
|
||||
let mockProjectService: Partial<ProjectService>;
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.projectService.findAllProjects();
|
||||
}
|
||||
beforeEach(async () => {
|
||||
mockProjectService = {
|
||||
create: jest.fn(),
|
||||
findAll: jest.fn(),
|
||||
findOne: jest.fn(),
|
||||
update: jest.fn(),
|
||||
remove: jest.fn(),
|
||||
findAllOrganizations: jest.fn(),
|
||||
};
|
||||
|
||||
@Get('organizations')
|
||||
findAllOrgs() {
|
||||
return this.projectService.findAllOrganizations();
|
||||
}
|
||||
}
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [ProjectController],
|
||||
providers: [
|
||||
{
|
||||
provide: ProjectService,
|
||||
useValue: mockProjectService,
|
||||
},
|
||||
],
|
||||
})
|
||||
// Override guards to avoid dependency issues
|
||||
.overrideGuard(JwtAuthGuard)
|
||||
.useValue({ canActivate: () => true })
|
||||
.overrideGuard(RbacGuard)
|
||||
.useValue({ canActivate: () => true })
|
||||
.compile();
|
||||
|
||||
controller = module.get<ProjectController>(ProjectController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
|
||||
describe('findAll', () => {
|
||||
it('should call projectService.findAll', async () => {
|
||||
const mockResult = { data: [], meta: {} };
|
||||
(mockProjectService.findAll as jest.Mock).mockResolvedValue(mockResult);
|
||||
|
||||
const result = await controller.findAll({});
|
||||
|
||||
expect(mockProjectService.findAll).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('findAllOrganizations', () => {
|
||||
it('should call projectService.findAllOrganizations', async () => {
|
||||
const mockOrgs = [{ organization_id: 1, name: 'Test Org' }];
|
||||
(mockProjectService.findAllOrganizations as jest.Mock).mockResolvedValue(
|
||||
mockOrgs
|
||||
);
|
||||
|
||||
const result = await controller.findAllOrgs();
|
||||
|
||||
expect(mockProjectService.findAllOrganizations).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,14 +12,14 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
|
||||
import { ProjectService } from './project.service.js';
|
||||
import { CreateProjectDto } from './dto/create-project.dto.js';
|
||||
import { UpdateProjectDto } from './dto/update-project.dto.js';
|
||||
import { SearchProjectDto } from './dto/search-project.dto.js';
|
||||
import { ProjectService } from './project.service';
|
||||
import { CreateProjectDto } from './dto/create-project.dto';
|
||||
import { UpdateProjectDto } from './dto/update-project.dto';
|
||||
import { SearchProjectDto } from './dto/search-project.dto';
|
||||
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard.js';
|
||||
import { RbacGuard } from '../../common/guards/rbac.guard.js';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator.js';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { RbacGuard } from '../../common/guards/rbac.guard';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
|
||||
@ApiTags('Projects')
|
||||
@ApiBearerAuth()
|
||||
@@ -49,6 +49,13 @@ export class ProjectController {
|
||||
return this.projectService.findAllOrganizations();
|
||||
}
|
||||
|
||||
@Get(':id/contracts')
|
||||
@ApiOperation({ summary: 'List All Contracts in Project' })
|
||||
@RequirePermission('project.view')
|
||||
findContracts(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.projectService.findContracts(id);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get Project Details' })
|
||||
@RequirePermission('project.view')
|
||||
@@ -61,7 +68,7 @@ export class ProjectController {
|
||||
@RequirePermission('project.edit')
|
||||
update(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() updateDto: UpdateProjectDto,
|
||||
@Body() updateDto: UpdateProjectDto
|
||||
) {
|
||||
return this.projectService.update(id, updateDto);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,49 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import { ProjectService } from './project.service';
|
||||
import { Project } from './entities/project.entity';
|
||||
import { Organization } from './entities/organization.entity';
|
||||
|
||||
describe('ProjectService', () => {
|
||||
let service: ProjectService;
|
||||
let mockProjectRepository: Record<string, jest.Mock>;
|
||||
let mockOrganizationRepository: Record<string, jest.Mock>;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockProjectRepository = {
|
||||
find: jest.fn(),
|
||||
findOne: jest.fn(),
|
||||
create: jest.fn(),
|
||||
save: jest.fn(),
|
||||
softDelete: jest.fn(),
|
||||
createQueryBuilder: jest.fn(() => ({
|
||||
leftJoinAndSelect: jest.fn().mockReturnThis(),
|
||||
where: jest.fn().mockReturnThis(),
|
||||
andWhere: jest.fn().mockReturnThis(),
|
||||
orderBy: jest.fn().mockReturnThis(),
|
||||
skip: jest.fn().mockReturnThis(),
|
||||
take: jest.fn().mockReturnThis(),
|
||||
getManyAndCount: jest.fn().mockResolvedValue([[], 0]),
|
||||
})),
|
||||
};
|
||||
|
||||
mockOrganizationRepository = {
|
||||
find: jest.fn(),
|
||||
findOne: jest.fn(),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ProjectService],
|
||||
providers: [
|
||||
ProjectService,
|
||||
{
|
||||
provide: getRepositoryToken(Project),
|
||||
useValue: mockProjectRepository,
|
||||
},
|
||||
{
|
||||
provide: getRepositoryToken(Organization),
|
||||
useValue: mockOrganizationRepository,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ProjectService>(ProjectService);
|
||||
@@ -15,4 +52,36 @@ describe('ProjectService', () => {
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe('findAll', () => {
|
||||
it('should return paginated projects', async () => {
|
||||
const mockProjects = [
|
||||
{
|
||||
project_id: 1,
|
||||
project_code: 'PROJ-001',
|
||||
project_name: 'Test Project',
|
||||
},
|
||||
];
|
||||
mockProjectRepository
|
||||
.createQueryBuilder()
|
||||
.getManyAndCount.mockResolvedValue([mockProjects, 1]);
|
||||
|
||||
const result = await service.findAll({});
|
||||
|
||||
expect(result.data).toBeDefined();
|
||||
expect(result.meta).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('findAllOrganizations', () => {
|
||||
it('should return all organizations', async () => {
|
||||
const mockOrgs = [{ organization_id: 1, name: 'Test Org' }];
|
||||
mockOrganizationRepository.find.mockResolvedValue(mockOrgs);
|
||||
|
||||
const result = await service.findAllOrganizations();
|
||||
|
||||
expect(mockOrganizationRepository.find).toHaveBeenCalled();
|
||||
expect(result).toEqual(mockOrgs);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ export class ProjectService {
|
||||
@InjectRepository(Project)
|
||||
private projectRepository: Repository<Project>,
|
||||
@InjectRepository(Organization)
|
||||
private organizationRepository: Repository<Organization>,
|
||||
private organizationRepository: Repository<Organization>
|
||||
) {}
|
||||
|
||||
// --- CRUD Operations ---
|
||||
@@ -36,7 +36,7 @@ export class ProjectService {
|
||||
});
|
||||
if (existing) {
|
||||
throw new ConflictException(
|
||||
`Project Code "${createDto.projectCode}" already exists`,
|
||||
`Project Code "${createDto.projectCode}" already exists`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export class ProjectService {
|
||||
if (search) {
|
||||
query.andWhere(
|
||||
'(project.projectCode LIKE :search OR project.projectName LIKE :search)',
|
||||
{ search: `%${search}%` },
|
||||
{ search: `%${search}%` }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -107,6 +107,19 @@ export class ProjectService {
|
||||
return this.projectRepository.softRemove(project);
|
||||
}
|
||||
|
||||
async findContracts(projectId: number) {
|
||||
const project = await this.projectRepository.findOne({
|
||||
where: { id: projectId },
|
||||
relations: ['contracts'],
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new NotFoundException(`Project ID ${projectId} not found`);
|
||||
}
|
||||
|
||||
return project.contracts;
|
||||
}
|
||||
|
||||
// --- Organization Helper ---
|
||||
|
||||
async findAllOrganizations() {
|
||||
|
||||
Reference in New Issue
Block a user