Adiciona busca por codauxiliar em findProducts e cria API de busca unificada
- Modifica findProducts para buscar por CODPROD e CODAUXILIAR - Adiciona testes para o método products - Cria endpoint unified-search para busca unificada por nome, código de barras ou codprod - Adiciona @IsOptional aos campos opcionais do ProductDetailQueryDto - Adiciona testes para products.service
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { DataConsultService } from '../data-consult.service';
|
||||
import { DataConsultRepository } from '../data-consult.repository';
|
||||
import { IRedisClient } from '../../core/configs/cache/IRedisClient';
|
||||
@@ -14,6 +15,8 @@ export const createMockRepository = (
|
||||
findSellers: jest.fn(),
|
||||
findBillings: jest.fn(),
|
||||
findCustomers: jest.fn(),
|
||||
findProducts: jest.fn(),
|
||||
findProductsByCodauxiliar: jest.fn(),
|
||||
findAllProducts: jest.fn(),
|
||||
findAllCarriers: jest.fn(),
|
||||
findRegions: jest.fn(),
|
||||
@@ -31,6 +34,9 @@ export interface DataConsultServiceTestContext {
|
||||
mockRepository: jest.Mocked<DataConsultRepository>;
|
||||
mockRedisClient: jest.Mocked<IRedisClient>;
|
||||
mockDataSource: jest.Mocked<DataSource>;
|
||||
mockLogger: {
|
||||
error: jest.Mock;
|
||||
};
|
||||
}
|
||||
|
||||
export async function createDataConsultServiceTestModule(
|
||||
@@ -64,10 +70,21 @@ export async function createDataConsultServiceTestModule(
|
||||
|
||||
const service = module.get<DataConsultService>(DataConsultService);
|
||||
|
||||
const mockLogger = {
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
jest.spyOn(Logger.prototype, 'error').mockImplementation(
|
||||
(message: any, ...optionalParams: any[]) => {
|
||||
mockLogger.error(message, ...optionalParams);
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
service,
|
||||
mockRepository,
|
||||
mockRedisClient,
|
||||
mockDataSource,
|
||||
mockLogger,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,6 +124,23 @@ describe('DataConsultService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter out sellers with null id', async () => {
|
||||
context.mockRepository.findSellers.mockResolvedValue([
|
||||
{ id: null, name: 'Vendedor 1' },
|
||||
{ id: '002', name: 'Vendedor 2' },
|
||||
{ id: null, name: 'Vendedor 3' },
|
||||
] as any);
|
||||
|
||||
const result = await context.service.sellers();
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('002');
|
||||
expect(result[0].name).toBe('Vendedor 2');
|
||||
result.forEach((seller) => {
|
||||
expect(seller.id).not.toBeNull();
|
||||
expect(seller.id).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should log error when repository throws exception', async () => {
|
||||
const repositoryError = new Error('Database connection failed');
|
||||
context.mockRepository.findSellers.mockRejectedValue(repositoryError);
|
||||
@@ -510,4 +527,124 @@ describe('DataConsultService', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('products', () => {
|
||||
let context: Awaited<ReturnType<typeof createDataConsultServiceTestModule>>;
|
||||
|
||||
beforeEach(async () => {
|
||||
context = await createDataConsultServiceTestModule();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Tests that expose problems', () => {
|
||||
it('should search products by CODPROD', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([
|
||||
{
|
||||
id: '12345',
|
||||
name: 'PRODUTO EXEMPLO',
|
||||
manufacturerCode: 'FAB001',
|
||||
},
|
||||
] as any);
|
||||
|
||||
const result = await context.service.products('12345');
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('12345');
|
||||
expect(result[0].name).toBe('PRODUTO EXEMPLO');
|
||||
expect(context.mockRepository.findProducts).toHaveBeenCalledWith(
|
||||
'12345',
|
||||
);
|
||||
});
|
||||
|
||||
it('should search products by CODAUXILIAR', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([
|
||||
{
|
||||
id: '12345',
|
||||
name: 'PRODUTO EXEMPLO',
|
||||
manufacturerCode: 'FAB001',
|
||||
},
|
||||
] as any);
|
||||
|
||||
const result = await context.service.products('7891234567890');
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('12345');
|
||||
expect(context.mockRepository.findProducts).toHaveBeenCalledWith(
|
||||
'7891234567890',
|
||||
);
|
||||
});
|
||||
|
||||
it('should search products by CODPROD or CODAUXILIAR', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([
|
||||
{
|
||||
id: '12345',
|
||||
name: 'PRODUTO EXEMPLO',
|
||||
manufacturerCode: 'FAB001',
|
||||
},
|
||||
{
|
||||
id: '12346',
|
||||
name: 'OUTRO PRODUTO',
|
||||
manufacturerCode: 'FAB002',
|
||||
},
|
||||
] as any);
|
||||
|
||||
const result = await context.service.products('12345');
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].id).toBe('12345');
|
||||
expect(result[1].id).toBe('12346');
|
||||
});
|
||||
|
||||
it('should handle empty result from repository', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([]);
|
||||
|
||||
const result = await context.service.products('99999');
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate that all products have required properties (id, name)', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([
|
||||
{ id: '12345', name: 'PRODUTO 1' },
|
||||
{ id: '12346', name: 'PRODUTO 2' },
|
||||
{ id: '12347', name: 'PRODUTO 3' },
|
||||
] as any);
|
||||
|
||||
const result = await context.service.products('12345');
|
||||
|
||||
result.forEach((product) => {
|
||||
expect(product.id).toBeDefined();
|
||||
expect(product.name).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error when filter is invalid', async () => {
|
||||
await expect(
|
||||
context.service.products(null as any),
|
||||
).rejects.toThrow(HttpException);
|
||||
await expect(
|
||||
context.service.products(undefined as any),
|
||||
).rejects.toThrow(HttpException);
|
||||
await expect(
|
||||
context.service.products('' as any),
|
||||
).rejects.toThrow(HttpException);
|
||||
});
|
||||
|
||||
it('should log error when repository throws exception', async () => {
|
||||
const repositoryError = new Error('Database connection failed');
|
||||
context.mockRepository.findProducts.mockRejectedValue(repositoryError);
|
||||
await expect(context.service.products('12345')).rejects.toThrow(
|
||||
HttpException,
|
||||
);
|
||||
expect(context.mockLogger.error).toHaveBeenCalledWith(
|
||||
'Erro ao buscar produtos',
|
||||
repositoryError,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -116,13 +116,15 @@ export class DataConsultRepository {
|
||||
}
|
||||
|
||||
async findProducts(filter: string): Promise<ProductDto[]> {
|
||||
const cleanedFilter = filter.replace(/\D/g, '');
|
||||
const sql = `
|
||||
SELECT PCPRODUT.CODPROD as "id",
|
||||
PCPRODUT.CODPROD || ' - ' || PCPRODUT.DESCRICAO || ' ( ' || PCPRODUT.CODFAB || ' )' as "description"
|
||||
FROM PCPRODUT
|
||||
WHERE PCPRODUT.CODPROD = :filter
|
||||
WHERE PCPRODUT.CODPROD = :0
|
||||
OR REGEXP_REPLACE(PCPRODUT.CODAUXILIAR, '[^0-9]', '') = :1
|
||||
`;
|
||||
const results = await this.executeQuery<ProductDto[]>(sql, [filter]);
|
||||
const results = await this.executeQuery<ProductDto[]>(sql, [filter, cleanedFilter]);
|
||||
return results.map((result) => new ProductDto(result));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user