import { mocked } from 'jest-mock' ; import { Test , TestingModule } from '@nestjs/testing' ; import { filteredByDto } from '@fc/common' ; import { LoggerLevelNames , LoggerService } from '@fc/logger' ; import { CsvParsingException } from '../exceptions' ; import { parseCsv } from '../helpers' ; import { CSV_VALIDATOR } from '../tokens' ; import { CsvService } from './csv.service' ; jest . mock ( '../helpers' ) ; jest . mock ( '@fc/common' , ( ) => ( { ... ( jest . requireActual ( '@fc/common' ) as any ) , filteredByDto : jest . fn ( ) , } ) ) ; interface MockInterface { dummy : string ; } describe ( 'CsvService' , ( ) => { let service ; class DtoMock { } const loggerServiceMock = { debug : jest . fn ( ) , fatal : jest . fn ( ) , setContext : jest . fn ( ) , trace : jest . fn ( ) , warn : jest . fn ( ) , } ; beforeEach ( async ( ) => { jest . resetAllMocks ( ) ; jest . restoreAllMocks ( ) ; const module = await Test . createTestingModule ( { providers : [ CsvService , LoggerService , { provide : CSV_VALIDATOR , useValue : DtoMock , } , ] , } ) . overrideProvider ( LoggerService ) . useValue ( loggerServiceMock ) . compile ( ) ; service = module . get < CsvService < MockInterface > > ( CsvService ) ; } ) ; it ( 'should be defined' , ( ) => { expect ( service ) . toBeDefined ( ) ; expect ( loggerServiceMock . setContext ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; describe ( 'pickData()' , ( ) => { const VALIDATOR_OPTIONS = { forbidNonWhitelisted : true , forbidUnknownValues : true , } ; let filteredByDtoMock ; const inputMock = { alpha : 'omega' , dummy : 'dummyValue' , } ; const filteredSuccessMock = { errors : [ ] , result : 'resultValue' , } ; const filteredFailedMock = { errors : [ new Error ( 'ValidationError' ) ] , result : null , } ; beforeEach ( ( ) => { filteredByDtoMock = mocked ( filteredByDto ) ; } ) ; it ( 'should log if filtered data by DTO is successfull' , async ( ) => { filteredByDtoMock . mockResolvedValueOnce ( filteredSuccessMock ) ; await service [ 'pickData' ] ( [ inputMock ] ) ; } ) ; it ( 'should successfully filtered data by DTO' , async ( ) => { filteredByDtoMock . mockResolvedValueOnce ( filteredSuccessMock ) ; const resultMock = [ filteredSuccessMock . result ] ; const result = await service [ 'pickData' ] ( [ inputMock ] ) ; expect ( result ) . toEqual ( resultMock ) ; expect ( filteredByDtoMock ) . toHaveBeenCalledTimes ( 1 ) ; expect ( filteredByDtoMock ) . toHaveBeenCalledWith ( inputMock , DtoMock , VALIDATOR_OPTIONS ) ; } ) ; it ( 'should fail to validate data by DTO' , async ( ) => { filteredByDtoMock . mockResolvedValueOnce ( filteredFailedMock ) ; const result = await service [ 'pickData' ] ( [ inputMock ] ) ; expect ( result ) . toStrictEqual ( [ ] ) ; expect ( filteredByDtoMock ) . toHaveBeenCalledTimes ( 1 ) ; expect ( filteredByDtoMock ) . toHaveBeenCalledWith ( inputMock , DtoMock , VALIDATOR_OPTIONS ) ; expect ( loggerServiceMock . warn ) . toHaveBeenCalledTimes ( 1 ) ; expect ( loggerServiceMock . warn ) . toHaveBeenCalledWith ( '"row n°1" was excluded from the result at DTO validation :[\n  {}\n]' ) ; } ) ; it ( 'should fail when dto crashed' , async ( ) => { const errorMock = new Error ( 'Unknown Error' ) ; filteredByDtoMock . mockRejectedValueOnce ( errorMock ) ; await expect ( service [ 'pickData' ] ( [ inputMock ] ) ) . rejects . toThrow ( errorMock ) ; expect ( loggerServiceMock . warn ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; } ) ; describe ( 'headersFilter()' , ( ) => { it ( 'should lowercase the headers of the csv' , ( ) => { const headers = [ 'A' , 'B' , 'C1' ] ; const resultMock = [ 'a' , 'b' , 'c1' ] ; const fn = service [ 'headersFilter' ] ( ) ; const result = fn ( headers ) ; expect ( result ) . toEqual ( resultMock ) ; } ) ; it ( 'should fail if headers contains void header' , ( ) => { const headers = [ 'A' , undefined , 'C1' ] ; expect ( ( ) => { const fn = service [ 'headersFilter' ] ( ) ; fn ( headers ) ; } ) . toThrow ( "Cannot read property 'toLowerCase' of undefined" ) ; } ) ; } ) ; describe ( 'parse()' , ( ) => { const fileMock = 'path/to/file.csv' ; let parseCsvMock ; let pickDataMock ; const csvMock = [ { dummy : 'dummyValue' , } , ] ; beforeEach ( ( ) => { jest . spyOn < CsvService < MockInterface > , any > ( service , 'headersFilter' ) ; pickDataMock = jest . spyOn < CsvService < MockInterface > , any > ( service , 'pickData' ) ; pickDataMock . mockResolvedValueOnce ( csvMock ) ; parseCsvMock = mocked ( parseCsv ) ; parseCsvMock . mockResolvedValueOnce ( csvMock ) ; } ) ; it ( 'should parse the data from CSV' , async ( ) => { const csvOptionsMock = { headers : expect . any ( Function ) , ignoreEmpty : true , trim : true , } ; await service . parse ( fileMock ) ; expect ( parseCsvMock ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parseCsvMock ) . toHaveBeenCalledWith ( fileMock , csvOptionsMock ) ; expect ( service [ 'collection' ] ) . toEqual ( csvMock ) ; } ) ; it ( 'should filter and validate data from CSV' , async ( ) => { await service . parse ( fileMock ) ; expect ( pickDataMock ) . toHaveBeenCalledTimes ( 1 ) ; expect ( pickDataMock ) . toHaveBeenCalledWith ( csvMock ) ; expect ( service [ 'collection' ] ) . toEqual ( csvMock ) ; } ) ; it ( 'should trace the data extracted from CSV' , async ( ) => { await service . parse ( fileMock ) ; expect ( loggerServiceMock . debug ) . toHaveBeenCalledTimes ( 1 ) ; expect ( loggerServiceMock . debug ) . toHaveBeenCalledWith ( `Loading collection...` ) ; expect ( loggerServiceMock . trace ) . toHaveBeenCalledTimes ( 1 ) ; expect ( loggerServiceMock . trace ) . toHaveBeenCalledWith ( { data : csvMock , path : fileMock , } ) ; } ) ; it ( 'should fail to validate data from csv' , async ( ) => { const errorMock = new Error ( 'Unknown Error' ) ; pickDataMock . mockReset ( ) . mockRejectedValueOnce ( [ errorMock ] ) ; await expect ( service . parse ( fileMock ) ) . rejects . toThrow ( CsvParsingException ) ; expect ( pickDataMock ) . toHaveBeenCalledTimes ( 1 ) ; expect ( pickDataMock ) . toHaveBeenCalledWith ( csvMock ) ; } ) ; it ( 'should fail if parsing csv crashed' , async ( ) => { const errorMock = new Error ( 'Unknown Error' ) ; parseCsvMock . mockReset ( ) . mockRejectedValueOnce ( errorMock ) ; await expect ( service . parse ( fileMock ) ) . rejects . toThrow ( CsvParsingException ) ; expect ( loggerServiceMock . trace ) . toHaveBeenCalledTimes ( 1 ) ; expect ( loggerServiceMock . trace ) . toHaveBeenCalledWith ( { error : errorMock , path : fileMock } , LoggerLevelNames . ERROR ) ; expect ( pickDataMock ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; } ) ; describe ( 'find()' , ( ) => { it ( 'should find in database with criteria' , async ( ) => { const correctMock = [ { dummy : 'hello' } ] ; const wrongMock = [ { hello : 'world' } , { dummy : 'world' } ] ; const inputs = [ ... correctMock , wrongMock ] as unknown as MockInterface [ ] ; service [ 'collection' ] = inputs ; const result = await service . find ( { dummy : 'hello' } ) ; expect ( result ) . toEqual ( correctMock [ 0 ] ) ; } ) ; it ( 'should find in database with criteria' , async ( ) => { const correctMock = [ { dummy : 'hello' } ] ; const wrongMock = [ { hello : 'world' } , { dummy : 'world' } ] ; const inputs = [ ... correctMock , wrongMock ] as unknown as MockInterface [ ] ; service [ 'collection' ] = inputs ; const result = await service . find ( { dummy : 'noValue' } ) ; expect ( result ) . toEqual ( null ) ; } ) ; } ) ; } ) ;