Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rebeccahum committed Jun 19, 2024
1 parent bd3c219 commit a16ed61
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 9 deletions.
124 changes: 124 additions & 0 deletions __tests__/bin/vip-app-deploy-validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { appDeployValidateCmd } from '../../src/bin/vip-app-deploy-validate';
import { getFileMeta } from '../../src/lib/client-file-uploader';
import * as exit from '../../src/lib/cli/exit';

Check failure on line 3 in __tests__/bin/vip-app-deploy-validate.js

View workflow job for this annotation

GitHub Actions / Lint

`../../src/lib/cli/exit` import should occur before import of `../../src/lib/client-file-uploader`
import { validateFile } from '../../src/lib/custom-deploy/custom-deploy';
import {
validateName,
validateZipFile,
validateTarFile,
} from '../../src/lib/validations/custom-deploy';

jest.mock( '../../src/lib/client-file-uploader', () => ( {
...jest.requireActual( '../../src/lib/client-file-uploader' ),
getFileMeta: jest
.fn()
.mockResolvedValue( { fileName: '/vip/skeleton.zip', basename: 'skeleton.zip' } ),
} ) );

jest.mock( '../../src/lib/custom-deploy/custom-deploy', () => ( {
validateFile: jest.fn(),
} ) );

jest.mock( '../../src/lib/validations/custom-deploy', () => ( {
validateZipFile: jest.fn(),
validateTarFile: jest.fn(),
} ) );

jest.mock( '../../src/lib/cli/command', () => {
const commandMock = {
argv: () => commandMock,
examples: () => commandMock,
option: () => commandMock,
command: () => commandMock,
};
return jest.fn( () => commandMock );
} );

const exitSpy = jest.spyOn( exit, 'withError' );
jest.spyOn( process, 'exit' ).mockImplementation( () => {} );
jest.spyOn( console, 'error' ).mockImplementation( () => {} );

const opts = {
app: {
id: 1,
organization: {
id: 2,
},
},
env: {
id: 3,
type: 'develop',
},
force: true,
};

describe( 'vip-app-deploy-validate', () => {
describe( 'validateName', () => {
beforeEach( async () => {
exitSpy.mockClear();
} );

it.each( [ '!client-mu-plugins', '..vip-go-skeleton', '*test' ] )(
'fails if the file has has invalid characters for directories',
basename => {
validateName( basename, true );
expect( exitSpy ).toHaveBeenCalledWith(
`Filename ${ basename } contains disallowed characters: [!:*?"<>|\'/^\\.\\.]+`

Check failure on line 66 in __tests__/bin/vip-app-deploy-validate.js

View workflow job for this annotation

GitHub Actions / Lint

Unnecessary escape character: \'
);
}
);

it.each( [ 'client-mu-plugins', 'vip-go-skeleton/', '._vip-go-skeleton' ] )(
'passes if the file has has valid characters for directories',
basename => {
validateName( basename, true );
expect( exitSpy ).not.toHaveBeenCalled();
}
);

it.each( [ 'client-mu-/plugins.php', 'vip-?config.php' ] )(
'fails if the file has has invalid characters for non-directories',
basename => {
validateName( basename, false );
expect( exitSpy ).toHaveBeenCalledWith(
`Filename ${ basename } contains disallowed characters: [!/:*?"<>|\'/^\\.\\.]+`

Check failure on line 84 in __tests__/bin/vip-app-deploy-validate.js

View workflow job for this annotation

GitHub Actions / Lint

Unnecessary escape character: \'
);
}
);

it.each( [ 'client-mu-plugins.php', 'vip-config.php' ] )(
'passes if the file has has valid characters for non-directories',
basename => {
validateName( basename, false );
expect( exitSpy ).not.toHaveBeenCalled();
}
);
} );

describe( 'appDeployValidateCmd', () => {
beforeEach( () => {
jest.clearAllMocks();
} );

it( 'should call expected functions (zip)', async () => {
const args = [ '/vip/skeleton.zip' ];
getFileMeta.mockResolvedValue( { fileName: '/vip/skeleton.zip', basename: 'skeleton.zip' } );

await appDeployValidateCmd( args, opts );
expect( validateFile ).toHaveBeenCalledTimes( 1 );
expect( validateZipFile ).toHaveBeenCalledTimes( 1 );
} );

it( 'should call expected functions (tar)', async () => {
const args = [ '/vip/foo.tar.gz' ];
getFileMeta.mockResolvedValue( {
fileName: '/vip/foo.tar.gz',
basename: 'foo.tar.gz',
} );

await appDeployValidateCmd( args, opts );
expect( validateFile ).toHaveBeenCalledTimes( 1 );
expect( validateTarFile ).toHaveBeenCalledTimes( 1 );
} );
} );
} );
3 changes: 2 additions & 1 deletion __tests__/bin/vip-app-deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jest.mock( '../../src/lib/cli/command', () => {
argv: () => commandMock,
examples: () => commandMock,
option: () => commandMock,
command: () => commandMock,
};
return jest.fn( () => commandMock );
} );
Expand Down Expand Up @@ -70,7 +71,7 @@ describe( 'vip-app-deploy', () => {
async basename => {
validateFilename( basename );
expect( exitSpy ).toHaveBeenCalledWith(
'Error: The characters used in the name of a file for custom deploys are limited to [0-9,a-z,A-Z,-,_,.]'
`Filename ${ basename } contains disallowed characters: [0-9,a-z,A-Z,-,_,.]`
);
}
);
Expand Down
1 change: 0 additions & 1 deletion src/lib/custom-deploy/custom-deploy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import fs from 'fs';
import gql from 'graphql-tag';
import debugLib from 'debug';

import API from '../../lib/api';
import * as exit from '../../lib/cli/exit';
Expand Down
11 changes: 4 additions & 7 deletions src/lib/validations/custom-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ import path from 'path';
import AdmZip from 'adm-zip';
import { constants } from 'node:fs';
import * as tar from 'tar';
import debugLib from 'debug';

import * as exit from '../../lib/cli/exit';

const debug = debugLib( '@automattic/vip:bin:vip-lib-deploy-validate' );

interface TarEntry {
path: string;
type: string;
Expand All @@ -20,7 +17,7 @@ const errorMessages = {
singleRootDir: 'The compressed file must contain a single root directory!',
invalidExt: 'Invalid file extension. Please provide a .zip, .tar.gz, or a .tgz file.',
invalidChars: ( filename: string, invalidChars: string ) =>
`Filename "${ filename }" contains disallowed characters: ${ invalidChars }`,
`Filename ${ filename } contains disallowed characters: ${ invalidChars }`,
};
const symlinkIgnorePattern = /\/node_modules\/[^/]+\/\.bin\//;
const macosxDir = '__MACOSX';
Expand Down Expand Up @@ -52,7 +49,7 @@ export function validateFilename( filename: string ) {
const re = /^[a-z0-9\-_.]+$/i;

if ( ! re.test( filename ) ) {
exit.withError( errorMessages.invalidChars( filename, re.toString() ) );
exit.withError( errorMessages.invalidChars( filename, '[0-9,a-z,A-Z,-,_,.]' ) );
}
}

Expand All @@ -62,13 +59,13 @@ export function validateFilename( filename: string ) {
* @param {string} name The name of the file
* @param {bool} isDirectory Whether the file is a directory
*/
function validateName( name: string, isDirectory: boolean ) {
export function validateName( name: string, isDirectory: boolean ) {
if ( name.startsWith( '._' ) ) {
return;
}

const invalidCharsPattern = isDirectory ? /[!\:*?"<>|']|^\.\..*$/ : /[!\/:*?"<>|']|^\.\..*$/;

Check failure on line 67 in src/lib/validations/custom-deploy.ts

View workflow job for this annotation

GitHub Actions / Lint

Unnecessary escape character: \:

Check failure on line 67 in src/lib/validations/custom-deploy.ts

View workflow job for this annotation

GitHub Actions / Lint

Unnecessary escape character: \/
const errorMessage = errorMessages.invalidChars( name, invalidCharsPattern.toString() );
const errorMessage = errorMessages.invalidChars( name, isDirectory ? '[!:*?"<>|\'/^\\.\\.]+' : '[!/:*?"<>|\'/^\\.\\.]+' );

Check failure on line 68 in src/lib/validations/custom-deploy.ts

View workflow job for this annotation

GitHub Actions / Lint

Replace `·name,·isDirectory·?·'[!:*?"<>|\'/^\\.\\.]+'·:·'[!/:*?"<>|\'/^\\.\\.]+'·` with `⏎↹↹name,⏎↹↹isDirectory·?·'[!:*?"<>|\'/^\\.\\.]+'·:·'[!/:*?"<>|\'/^\\.\\.]+'⏎↹`
if ( invalidCharsPattern.test( name ) ) {
exit.withError( errorMessage );
}
Expand Down

0 comments on commit a16ed61

Please sign in to comment.