Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gracefully fail when directory is not detected #1947

Merged
merged 13 commits into from
Jul 29, 2024
140 changes: 140 additions & 0 deletions __tests__/bin/vip-import-validate-files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import chalk from 'chalk';

import { vipImportValidateFilesCmd } from '../../src/bin/vip-import-validate-files';
import { getMediaImportConfig } from '../../src/lib/media-import/config';
import {
findNestedDirectories,
isDirectory,
validateFiles,
logErrors,
} from '../../src/lib/vip-import-validate-files';

// Mock external dependencies
jest.mock( 'chalk', () => ( {
red: jest.fn( msg => msg ),
} ) );

jest.mock( 'url', () => ( {
parse: jest.fn( url => ( { path: url } ) ),
} ) );

// Mock internal dependencies
jest.mock( '../../src/lib/cli/command', () => {
const commandMock = {
argv: () => commandMock,
examples: () => commandMock,
option: () => commandMock,
};
return jest.fn( () => commandMock );
} );
jest.mock( '../../src/lib/media-import/config', () => ( {
getMediaImportConfig: jest.fn(),
} ) );
jest.mock( '../../src/lib/tracker', () => ( {
trackEvent: jest.fn(),
} ) );
jest.mock( '../../src/lib/vip-import-validate-files', () => ( {
findNestedDirectories: jest.fn(),
folderStructureValidation: jest.fn(),
isDirectory: jest.fn(),
summaryLogs: jest.fn(),
validateFiles: jest.fn(),
logErrors: jest.fn(),
ValidateFilesErrors: {
INVALID_TYPES: 'INVALID_TYPES',
INVALID_SIZES: 'INVALID_SIZES',
INVALID_NAME_CHARACTER_COUNTS: 'INVALID_NAME_CHARACTER_COUNTS',
INVALID_NAMES: 'INVALID_NAMES',
INTERMEDIATE_IMAGES: 'INTERMEDIATE_IMAGES',
},
} ) );

describe( 'vipImportValidateFilesCmd', () => {
afterEach( () => {
jest.clearAllMocks();
} );

it( 'should log an error if the given path is not a directory', async () => {
isDirectory.mockResolvedValue( false );

console.error = jest.fn();

await vipImportValidateFilesCmd( [ 'wp-content/uploads/valid-invalid-path' ] );

expect( console.error ).toHaveBeenCalledWith(
chalk.red( '✕ Error:' ),
'The given path is not a directory, please provide a valid directory path.'
);
} );

it( 'should terminate if no nested files are found', async () => {
isDirectory.mockResolvedValue( true );
findNestedDirectories.mockReturnValue( null );

await vipImportValidateFilesCmd( [ 'wp-content/uploads/valid-path' ] );

expect( findNestedDirectories ).toHaveBeenCalledWith( 'wp-content/uploads/valid-path' );
} );

it( 'should log an error if media files directory is empty', async () => {
isDirectory.mockResolvedValue( true );
// there's no file in the directory
findNestedDirectories.mockReturnValue( { files: [], folderStructureObj: {} } );

console.error = jest.fn();

await vipImportValidateFilesCmd( [ 'wp-content/uploads/empty-directory' ] );

expect( console.error ).toHaveBeenCalledWith(
chalk.red( '✕ Error:' ),
'Media files directory cannot be empty'
);
} );

it( 'should log an error if media import config cannot be retrieved', async () => {
isDirectory.mockResolvedValue( true );
findNestedDirectories.mockReturnValue( { files: [ 'any-file1.jpg' ], folderStructureObj: {} } );
// getMediaImportConfig fails
getMediaImportConfig.mockResolvedValue( null );

console.error = jest.fn();

await vipImportValidateFilesCmd( [ 'wp-content/uploads/valid-directory' ] );

expect( console.error ).toHaveBeenCalledWith(
chalk.red( '✕ Error:' ),
'Could not retrieve validation metadata. Please contact VIP Support.'
);
} );

it( 'should call validateFiles and log errors', async () => {
isDirectory.mockResolvedValue( true );
findNestedDirectories.mockReturnValue( {
files: [ 'not-any-file1.jpg' ],
folderStructureObj: {},
} );
getMediaImportConfig.mockResolvedValue( {
allowedFileTypes: {},
fileSizeLimitInBytes: 1000,
fileNameCharCount: 255,
} );
validateFiles.mockResolvedValue( {
intermediateImagesTotal: 0,
errorFileTypes: [],
errorFileNames: [],
errorFileSizes: [],
errorFileNamesCharCount: [],
intermediateImages: {},
} );

await vipImportValidateFilesCmd( [ 'wp-content/uploads/valid-directory' ] );

expect( validateFiles ).toHaveBeenCalledWith( [ 'not-any-file1.jpg' ], {
allowedFileTypes: {},
fileSizeLimitInBytes: 1000,
fileNameCharCount: 255,
} );

expect( logErrors ).toHaveBeenCalledTimes( 5 ); // Log errors for all 5 types
} );
} );
46 changes: 46 additions & 0 deletions __tests__/lib/vip-import-validate-files.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
isFileSanitized,
validateFiles,
logErrors,
findNestedDirectories,
} from '../../src/lib/vip-import-validate-files';

global.console = { log: jest.fn(), error: jest.fn() };
Expand Down Expand Up @@ -214,4 +215,49 @@ describe( 'lib/vip-import-validate-files', () => {
expect( mockConsoleError ).not.toHaveBeenCalled();
} );
} );
describe( 'findNestedDirectories()', () => {
// Mocking file system and chalk
jest.mock( 'fs' );
jest.mock( 'chalk', () => ( {
red: jest.fn( () => 'red' ),
} ) );

let readdirSyncMock;
let statSyncMock;

beforeEach( () => {
readdirSyncMock = jest.spyOn( fs, 'readdirSync' );
statSyncMock = jest.spyOn( fs, 'statSync' );
} );

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

it( 'should return undefined and log an error if the directory cannot be read', () => {
const errorMessage = 'Reason: ENOTDIR: not a directory, scandir ~/Downloads/wp-content.zip';
readdirSyncMock.mockImplementation( () => {
throw new Error( errorMessage );
} );

console.error = jest.fn();

const result = findNestedDirectories( '~/Downloads/wp-content.zip' );

expect( result ).toBeUndefined();
expect( console.error ).toHaveBeenCalledWith(
chalk.red( '✕' ),
` Error: Cannot read nested directory: ~/Downloads/wp-content.zip. Reason: ${ errorMessage }`
);
} );

it( 'should return an empty result for an empty directory', () => {
readdirSyncMock.mockReturnValue( [] );
statSyncMock.mockReturnValue( { isDirectory: () => false } );

const result = findNestedDirectories( '/empty/dir' );

expect( result ).toEqual( { files: [], folderStructureObj: {} } );
} );
} );
} );
Loading