2023-09-20 05:40:06 -07:00
import { mkdtempSync , readFileSync } from 'fs' ;
import type { IncomingMessage } from 'http' ;
2024-09-30 06:38:56 -07:00
import type { Agent } from 'https' ;
2023-04-12 05:58:05 -07:00
import { mock } from 'jest-mock-extended' ;
2024-09-30 06:38:56 -07:00
import toPlainObject from 'lodash/toPlainObject' ;
import { DateTime } from 'luxon' ;
2023-04-12 05:58:05 -07:00
import type {
IBinaryData ,
2024-02-22 08:56:48 -08:00
IHttpRequestMethods ,
2024-03-14 04:17:20 -07:00
IHttpRequestOptions ,
2023-04-12 05:58:05 -07:00
INode ,
2024-03-14 04:17:20 -07:00
IRequestOptions ,
2023-04-12 05:58:05 -07:00
ITaskDataConnections ,
IWorkflowExecuteAdditionalData ,
2024-03-18 10:35:49 -07:00
NodeParameterValue ,
2023-04-12 05:58:05 -07:00
Workflow ,
WorkflowHooks ,
} from 'n8n-workflow' ;
2024-06-18 03:50:44 -07:00
import { ExpressionError } from 'n8n-workflow' ;
2023-09-20 05:40:06 -07:00
import nock from 'nock' ;
import { tmpdir } from 'os' ;
import { join } from 'path' ;
2024-09-30 06:38:56 -07:00
import type { SecureContextOptions } from 'tls' ;
2023-09-22 08:22:12 -07:00
import Container from 'typedi' ;
2024-09-30 06:38:56 -07:00
import { BinaryDataService } from '@/BinaryData/BinaryData.service' ;
2024-08-09 07:40:50 -07:00
import { InstanceSettings } from '@/InstanceSettings' ;
2024-09-30 06:38:56 -07:00
import {
cleanupParameterData ,
copyInputItems ,
ensureType ,
getBinaryDataBuffer ,
isFilePathBlocked ,
parseIncomingMessage ,
parseRequestObject ,
proxyRequestToAxios ,
removeEmptyBody ,
setBinaryDataBuffer ,
} from '@/NodeExecuteFunctions' ;
2022-09-11 07:42:09 -07:00
const temporaryDir = mkdtempSync ( join ( tmpdir ( ) , 'n8n' ) ) ;
describe ( 'NodeExecuteFunctions' , ( ) = > {
2023-04-12 05:58:05 -07:00
describe ( 'test binary data helper methods' , ( ) = > {
2023-05-02 01:37:19 -07:00
test ( "test getBinaryDataBuffer(...) & setBinaryDataBuffer(...) methods in 'default' mode" , async ( ) = > {
2022-09-11 07:42:09 -07:00
// Setup a 'default' binary data manager instance
2023-09-22 08:22:12 -07:00
Container . set ( BinaryDataService , new BinaryDataService ( ) ) ;
await Container . get ( BinaryDataService ) . init ( {
2022-09-11 07:42:09 -07:00
mode : 'default' ,
2023-09-22 08:22:12 -07:00
availableModes : [ 'default' ] ,
2022-09-11 07:42:09 -07:00
localStoragePath : temporaryDir ,
} ) ;
// Set our binary data buffer
2023-05-02 01:37:19 -07:00
const inputData : Buffer = Buffer . from ( 'This is some binary data' , 'utf8' ) ;
const setBinaryDataBufferResponse : IBinaryData = await setBinaryDataBuffer (
2022-09-11 07:42:09 -07:00
{
mimeType : 'txt' ,
data : 'This should be overwritten by the actual payload in the response' ,
} ,
inputData ,
2023-09-27 00:42:35 -07:00
'workflowId' ,
2022-09-11 07:42:09 -07:00
'executionId' ,
) ;
// Expect our return object to contain the base64 encoding of the input data, as it should be stored in memory.
expect ( setBinaryDataBufferResponse . data ) . toEqual ( inputData . toString ( 'base64' ) ) ;
// Now, re-fetch our data.
// An ITaskDataConnections object is used to share data between nodes. The top level property, 'main', represents the successful output object from a previous node.
2023-05-02 01:37:19 -07:00
const taskDataConnectionsInput : ITaskDataConnections = {
2022-09-11 07:42:09 -07:00
main : [ ] ,
} ;
// We add an input set, with one item at index 0, to this input. It contains an empty json payload and our binary data.
taskDataConnectionsInput . main . push ( [
{
json : { } ,
binary : {
data : setBinaryDataBufferResponse ,
} ,
} ,
] ) ;
// Now, lets fetch our data! The item will be item index 0.
2023-05-02 01:37:19 -07:00
const getBinaryDataBufferResponse : Buffer = await getBinaryDataBuffer (
2022-09-11 07:42:09 -07:00
taskDataConnectionsInput ,
0 ,
'data' ,
0 ,
) ;
expect ( getBinaryDataBufferResponse ) . toEqual ( inputData ) ;
} ) ;
2023-05-02 01:37:19 -07:00
test ( "test getBinaryDataBuffer(...) & setBinaryDataBuffer(...) methods in 'filesystem' mode" , async ( ) = > {
2023-09-22 08:22:12 -07:00
Container . set ( BinaryDataService , new BinaryDataService ( ) ) ;
2022-09-11 07:42:09 -07:00
// Setup a 'filesystem' binary data manager instance
2023-09-22 08:22:12 -07:00
await Container . get ( BinaryDataService ) . init ( {
2022-09-11 07:42:09 -07:00
mode : 'filesystem' ,
2023-09-22 08:22:12 -07:00
availableModes : [ 'filesystem' ] ,
2022-09-11 07:42:09 -07:00
localStoragePath : temporaryDir ,
} ) ;
// Set our binary data buffer
2023-05-02 01:37:19 -07:00
const inputData : Buffer = Buffer . from ( 'This is some binary data' , 'utf8' ) ;
const setBinaryDataBufferResponse : IBinaryData = await setBinaryDataBuffer (
2022-09-11 07:42:09 -07:00
{
mimeType : 'txt' ,
data : 'This should be overwritten with the name of the configured data manager' ,
} ,
inputData ,
2023-09-27 00:42:35 -07:00
'workflowId' ,
2022-09-11 07:42:09 -07:00
'executionId' ,
) ;
// Expect our return object to contain the name of the configured data manager.
2023-10-10 01:06:06 -07:00
expect ( setBinaryDataBufferResponse . data ) . toEqual ( 'filesystem-v2' ) ;
2022-09-11 07:42:09 -07:00
// Ensure that the input data was successfully persisted to disk.
expect (
readFileSync (
2023-10-10 01:06:06 -07:00
` ${ temporaryDir } / ${ setBinaryDataBufferResponse . id ? . replace ( 'filesystem-v2:' , '' ) } ` ,
2022-09-11 07:42:09 -07:00
) ,
) . toEqual ( inputData ) ;
// Now, re-fetch our data.
// An ITaskDataConnections object is used to share data between nodes. The top level property, 'main', represents the successful output object from a previous node.
2023-05-02 01:37:19 -07:00
const taskDataConnectionsInput : ITaskDataConnections = {
2022-09-11 07:42:09 -07:00
main : [ ] ,
} ;
// We add an input set, with one item at index 0, to this input. It contains an empty json payload and our binary data.
taskDataConnectionsInput . main . push ( [
{
json : { } ,
binary : {
data : setBinaryDataBufferResponse ,
} ,
} ,
] ) ;
// Now, lets fetch our data! The item will be item index 0.
2023-05-02 01:37:19 -07:00
const getBinaryDataBufferResponse : Buffer = await getBinaryDataBuffer (
2022-09-11 07:42:09 -07:00
taskDataConnectionsInput ,
0 ,
'data' ,
0 ,
) ;
expect ( getBinaryDataBufferResponse ) . toEqual ( inputData ) ;
} ) ;
} ) ;
2023-04-12 05:58:05 -07:00
2023-09-20 05:40:06 -07:00
describe ( 'parseIncomingMessage' , ( ) = > {
it ( 'parses valid content-type header' , ( ) = > {
const message = mock < IncomingMessage > ( {
headers : { 'content-type' : 'application/json' , 'content-disposition' : undefined } ,
} ) ;
parseIncomingMessage ( message ) ;
expect ( message . contentType ) . toEqual ( 'application/json' ) ;
} ) ;
it ( 'parses valid content-type header with parameters' , ( ) = > {
const message = mock < IncomingMessage > ( {
headers : {
'content-type' : 'application/json; charset=utf-8' ,
'content-disposition' : undefined ,
} ,
} ) ;
parseIncomingMessage ( message ) ;
expect ( message . contentType ) . toEqual ( 'application/json' ) ;
} ) ;
it ( 'parses valid content-disposition header with filename*' , ( ) = > {
const message = mock < IncomingMessage > ( {
headers : {
'content-type' : undefined ,
'content-disposition' :
'attachment; filename="screenshot%20(1).png"; filename*=UTF-8\'\'screenshot%20(1).png' ,
} ,
} ) ;
parseIncomingMessage ( message ) ;
expect ( message . contentDisposition ) . toEqual ( {
filename : 'screenshot (1).png' ,
type : 'attachment' ,
} ) ;
} ) ;
2023-09-21 05:54:10 -07:00
it ( 'parses valid content-disposition header with filename* (quoted)' , ( ) = > {
const message = mock < IncomingMessage > ( {
headers : {
'content-type' : undefined ,
'content-disposition' : ' attachment;filename*="utf-8\' \'test-unsplash.jpg"' ,
} ,
} ) ;
parseIncomingMessage ( message ) ;
expect ( message . contentDisposition ) . toEqual ( {
filename : 'test-unsplash.jpg' ,
type : 'attachment' ,
} ) ;
} ) ;
2023-09-20 05:40:06 -07:00
it ( 'parses valid content-disposition header with filename and trailing ";"' , ( ) = > {
const message = mock < IncomingMessage > ( {
headers : {
'content-type' : undefined ,
'content-disposition' : 'inline; filename="screenshot%20(1).png";' ,
} ,
} ) ;
parseIncomingMessage ( message ) ;
expect ( message . contentDisposition ) . toEqual ( {
filename : 'screenshot (1).png' ,
type : 'inline' ,
} ) ;
} ) ;
it ( 'parses non standard content-disposition with missing type' , ( ) = > {
const message = mock < IncomingMessage > ( {
headers : {
'content-type' : undefined ,
'content-disposition' : 'filename="screenshot%20(1).png";' ,
} ,
} ) ;
parseIncomingMessage ( message ) ;
expect ( message . contentDisposition ) . toEqual ( {
filename : 'screenshot (1).png' ,
type : 'attachment' ,
} ) ;
} ) ;
} ) ;
2023-04-12 05:58:05 -07:00
describe ( 'proxyRequestToAxios' , ( ) = > {
const baseUrl = 'http://example.de' ;
const workflow = mock < Workflow > ( ) ;
const hooks = mock < WorkflowHooks > ( ) ;
const additionalData = mock < IWorkflowExecuteAdditionalData > ( { hooks } ) ;
const node = mock < INode > ( ) ;
beforeEach ( ( ) = > {
hooks . executeHookFunctions . mockClear ( ) ;
} ) ;
2024-04-03 09:00:27 -07:00
test ( 'should rethrow an error with `status` property' , async ( ) = > {
nock ( baseUrl ) . get ( '/test' ) . reply ( 400 ) ;
try {
await proxyRequestToAxios ( workflow , additionalData , node , ` ${ baseUrl } /test ` ) ;
} catch ( error ) {
expect ( error . status ) . toEqual ( 400 ) ;
}
} ) ;
2023-04-12 05:58:05 -07:00
test ( 'should not throw if the response status is 200' , async ( ) = > {
nock ( baseUrl ) . get ( '/test' ) . reply ( 200 ) ;
await proxyRequestToAxios ( workflow , additionalData , node , ` ${ baseUrl } /test ` ) ;
expect ( hooks . executeHookFunctions ) . toHaveBeenCalledWith ( 'nodeFetchedData' , [
workflow . id ,
node ,
] ) ;
} ) ;
test ( 'should throw if the response status is 403' , async ( ) = > {
const headers = { 'content-type' : 'text/plain' } ;
nock ( baseUrl ) . get ( '/test' ) . reply ( 403 , 'Forbidden' , headers ) ;
try {
await proxyRequestToAxios ( workflow , additionalData , node , ` ${ baseUrl } /test ` ) ;
} catch ( error ) {
expect ( error . statusCode ) . toEqual ( 403 ) ;
expect ( error . request ) . toBeUndefined ( ) ;
expect ( error . response ) . toMatchObject ( { headers , status : 403 } ) ;
expect ( error . options ) . toMatchObject ( {
headers : { Accept : '*/*' } ,
method : 'get' ,
url : 'http://example.de/test' ,
} ) ;
expect ( error . config ) . toBeUndefined ( ) ;
expect ( error . message ) . toEqual ( '403 - "Forbidden"' ) ;
}
expect ( hooks . executeHookFunctions ) . not . toHaveBeenCalled ( ) ;
} ) ;
test ( 'should not throw if the response status is 404, but `simple` option is set to `false`' , async ( ) = > {
nock ( baseUrl ) . get ( '/test' ) . reply ( 404 , 'Not Found' ) ;
const response = await proxyRequestToAxios ( workflow , additionalData , node , {
url : ` ${ baseUrl } /test ` ,
simple : false ,
} ) ;
expect ( response ) . toEqual ( 'Not Found' ) ;
expect ( hooks . executeHookFunctions ) . toHaveBeenCalledWith ( 'nodeFetchedData' , [
workflow . id ,
node ,
] ) ;
} ) ;
test ( 'should return full response when `resolveWithFullResponse` is set to true' , async ( ) = > {
nock ( baseUrl ) . get ( '/test' ) . reply ( 404 , 'Not Found' ) ;
const response = await proxyRequestToAxios ( workflow , additionalData , node , {
url : ` ${ baseUrl } /test ` ,
resolveWithFullResponse : true ,
simple : false ,
} ) ;
expect ( response ) . toMatchObject ( {
body : 'Not Found' ,
headers : { } ,
statusCode : 404 ,
statusMessage : null ,
} ) ;
expect ( hooks . executeHookFunctions ) . toHaveBeenCalledWith ( 'nodeFetchedData' , [
workflow . id ,
node ,
] ) ;
} ) ;
2024-01-31 06:11:29 -08:00
describe ( 'redirects' , ( ) = > {
test ( 'should forward authorization header' , async ( ) = > {
nock ( baseUrl ) . get ( '/redirect' ) . reply ( 301 , '' , { Location : 'https://otherdomain.com/test' } ) ;
nock ( 'https://otherdomain.com' )
. get ( '/test' )
. reply ( 200 , function ( ) {
return this . req . headers ;
} ) ;
const response = await proxyRequestToAxios ( workflow , additionalData , node , {
url : ` ${ baseUrl } /redirect ` ,
auth : {
username : 'testuser' ,
password : 'testpassword' ,
} ,
headers : {
'X-Other-Header' : 'otherHeaderContent' ,
} ,
resolveWithFullResponse : true ,
} ) ;
expect ( response . statusCode ) . toBe ( 200 ) ;
const forwardedHeaders = JSON . parse ( response . body ) ;
expect ( forwardedHeaders . authorization ) . toBe ( 'Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk' ) ;
expect ( forwardedHeaders [ 'x-other-header' ] ) . toBe ( 'otherHeaderContent' ) ;
} ) ;
test ( 'should follow redirects by default' , async ( ) = > {
nock ( baseUrl )
. get ( '/redirect' )
. reply ( 301 , '' , { Location : ` ${ baseUrl } /test ` } ) ;
nock ( baseUrl ) . get ( '/test' ) . reply ( 200 , 'Redirected' ) ;
const response = await proxyRequestToAxios ( workflow , additionalData , node , {
url : ` ${ baseUrl } /redirect ` ,
resolveWithFullResponse : true ,
} ) ;
expect ( response ) . toMatchObject ( {
body : 'Redirected' ,
headers : { } ,
statusCode : 200 ,
} ) ;
} ) ;
test ( 'should not follow redirects when configured' , async ( ) = > {
nock ( baseUrl )
. get ( '/redirect' )
. reply ( 301 , '' , { Location : ` ${ baseUrl } /test ` } ) ;
nock ( baseUrl ) . get ( '/test' ) . reply ( 200 , 'Redirected' ) ;
await expect (
proxyRequestToAxios ( workflow , additionalData , node , {
url : ` ${ baseUrl } /redirect ` ,
resolveWithFullResponse : true ,
followRedirect : false ,
} ) ,
) . rejects . toThrowError ( expect . objectContaining ( { statusCode : 301 } ) ) ;
} ) ;
} ) ;
2023-04-12 05:58:05 -07:00
} ) ;
2023-10-06 07:25:58 -07:00
2024-02-06 10:38:36 -08:00
describe ( 'parseRequestObject' , ( ) = > {
test ( 'should not use Host header for SNI' , async ( ) = > {
const axiosOptions = await parseRequestObject ( {
url : 'https://example.de/foo/bar' ,
headers : { Host : 'other.host.com' } ,
} ) ;
expect ( ( axiosOptions . httpsAgent as Agent ) . options . servername ) . toEqual ( 'example.de' ) ;
} ) ;
2024-02-22 08:56:48 -08:00
2024-04-24 07:28:02 -07:00
describe ( 'should set SSL certificates' , ( ) = > {
const agentOptions : SecureContextOptions = {
ca : '-----BEGIN CERTIFICATE-----\nTEST\n-----END CERTIFICATE-----' ,
} ;
const requestObject : IRequestOptions = {
method : 'GET' ,
uri : 'https://example.de' ,
agentOptions ,
} ;
test ( 'on regular requests' , async ( ) = > {
const axiosOptions = await parseRequestObject ( requestObject ) ;
expect ( ( axiosOptions . httpsAgent as Agent ) . options ) . toEqual ( {
servername : 'example.de' ,
. . . agentOptions ,
noDelay : true ,
path : null ,
} ) ;
} ) ;
test ( 'on redirected requests' , async ( ) = > {
const axiosOptions = await parseRequestObject ( requestObject ) ;
expect ( axiosOptions . beforeRedirect ) . toBeDefined ;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const redirectOptions : Record < string , any > = { agents : { } , hostname : 'example.de' } ;
axiosOptions . beforeRedirect ! ( redirectOptions , mock ( ) ) ;
expect ( redirectOptions . agent ) . toEqual ( redirectOptions . agents . https ) ;
expect ( ( redirectOptions . agent as Agent ) . options ) . toEqual ( {
servername : 'example.de' ,
. . . agentOptions ,
noDelay : true ,
path : null ,
} ) ;
} ) ;
} ) ;
2024-02-22 08:56:48 -08:00
describe ( 'when followRedirect is true' , ( ) = > {
test . each ( [ 'GET' , 'HEAD' ] as IHttpRequestMethods [ ] ) (
'should set maxRedirects on %s ' ,
async ( method ) = > {
const axiosOptions = await parseRequestObject ( {
method ,
followRedirect : true ,
maxRedirects : 1234 ,
} ) ;
expect ( axiosOptions . maxRedirects ) . toEqual ( 1234 ) ;
} ,
) ;
test . each ( [ 'POST' , 'PUT' , 'PATCH' , 'DELETE' ] as IHttpRequestMethods [ ] ) (
'should not set maxRedirects on %s ' ,
async ( method ) = > {
const axiosOptions = await parseRequestObject ( {
method ,
followRedirect : true ,
maxRedirects : 1234 ,
} ) ;
expect ( axiosOptions . maxRedirects ) . toEqual ( 0 ) ;
} ,
) ;
} ) ;
describe ( 'when followAllRedirects is true' , ( ) = > {
test . each ( [ 'GET' , 'HEAD' , 'POST' , 'PUT' , 'PATCH' , 'DELETE' ] as IHttpRequestMethods [ ] ) (
'should set maxRedirects on %s ' ,
async ( method ) = > {
const axiosOptions = await parseRequestObject ( {
method ,
followAllRedirects : true ,
maxRedirects : 1234 ,
} ) ;
expect ( axiosOptions . maxRedirects ) . toEqual ( 1234 ) ;
} ,
) ;
} ) ;
2024-02-06 10:38:36 -08:00
} ) ;
2024-03-18 10:35:49 -07:00
describe ( 'cleanupParameterData' , ( ) = > {
it ( 'should stringify Luxon dates in-place' , ( ) = > {
const input = { x : 1 , y : DateTime.now ( ) as unknown as NodeParameterValue } ;
expect ( typeof input . y ) . toBe ( 'object' ) ;
cleanupParameterData ( input ) ;
expect ( typeof input . y ) . toBe ( 'string' ) ;
} ) ;
2024-03-25 01:44:10 -07:00
it ( 'should stringify plain Luxon dates in-place' , ( ) = > {
const input = {
x : 1 ,
y : toPlainObject ( DateTime . now ( ) ) ,
} ;
expect ( typeof input . y ) . toBe ( 'object' ) ;
cleanupParameterData ( input ) ;
expect ( typeof input . y ) . toBe ( 'string' ) ;
} ) ;
2024-03-18 10:35:49 -07:00
it ( 'should handle objects with nameless constructors' , ( ) = > {
const input = { x : 1 , y : { constructor : { } } as NodeParameterValue } ;
expect ( typeof input . y ) . toBe ( 'object' ) ;
cleanupParameterData ( input ) ;
expect ( typeof input . y ) . toBe ( 'object' ) ;
} ) ;
it ( 'should handle objects without a constructor' , ( ) = > {
const input = { x : 1 , y : { constructor : undefined } as unknown as NodeParameterValue } ;
expect ( typeof input . y ) . toBe ( 'object' ) ;
cleanupParameterData ( input ) ;
expect ( typeof input . y ) . toBe ( 'object' ) ;
} ) ;
} ) ;
2023-10-06 07:25:58 -07:00
describe ( 'copyInputItems' , ( ) = > {
it ( 'should pick only selected properties' , ( ) = > {
const output = copyInputItems (
[
{
json : {
a : 1 ,
b : true ,
c : { } ,
} ,
} ,
] ,
[ 'a' ] ,
) ;
expect ( output ) . toEqual ( [ { a : 1 } ] ) ;
} ) ;
it ( 'should convert undefined to null' , ( ) = > {
const output = copyInputItems (
[
{
json : {
a : undefined ,
} ,
} ,
] ,
[ 'a' ] ,
) ;
expect ( output ) . toEqual ( [ { a : null } ] ) ;
} ) ;
it ( 'should clone objects' , ( ) = > {
const input = {
a : { b : 5 } ,
} ;
const output = copyInputItems (
[
{
json : input ,
} ,
] ,
[ 'a' ] ,
) ;
expect ( output [ 0 ] . a ) . toEqual ( input . a ) ;
expect ( output [ 0 ] . a === input . a ) . toEqual ( false ) ;
} ) ;
} ) ;
2024-03-14 04:17:20 -07:00
describe ( 'removeEmptyBody' , ( ) = > {
test . each ( [ 'GET' , 'HEAD' , 'OPTIONS' ] as IHttpRequestMethods [ ] ) (
'Should remove empty body for %s' ,
async ( method ) = > {
const requestOptions = {
method ,
body : { } ,
} as IHttpRequestOptions | IRequestOptions ;
removeEmptyBody ( requestOptions ) ;
expect ( requestOptions . body ) . toEqual ( undefined ) ;
} ,
) ;
test . each ( [ 'GET' , 'HEAD' , 'OPTIONS' ] as IHttpRequestMethods [ ] ) (
'Should not remove non-empty body for %s' ,
async ( method ) = > {
const requestOptions = {
method ,
body : { test : true } ,
} as IHttpRequestOptions | IRequestOptions ;
removeEmptyBody ( requestOptions ) ;
expect ( requestOptions . body ) . toEqual ( { test : true } ) ;
} ,
) ;
test . each ( [ 'POST' , 'PUT' , 'PATCH' , 'DELETE' ] as IHttpRequestMethods [ ] ) (
'Should not remove empty body for %s' ,
async ( method ) = > {
const requestOptions = {
method ,
body : { } ,
} as IHttpRequestOptions | IRequestOptions ;
removeEmptyBody ( requestOptions ) ;
expect ( requestOptions . body ) . toEqual ( { } ) ;
} ,
) ;
} ) ;
2024-06-18 03:50:44 -07:00
describe ( 'ensureType' , ( ) = > {
it ( 'throws error for null value' , ( ) = > {
expect ( ( ) = > ensureType ( 'string' , null , 'myParam' ) ) . toThrowError (
new ExpressionError ( "Parameter 'myParam' must not be null" ) ,
) ;
} ) ;
it ( 'throws error for undefined value' , ( ) = > {
expect ( ( ) = > ensureType ( 'string' , undefined , 'myParam' ) ) . toThrowError (
new ExpressionError ( "Parameter 'myParam' could not be 'undefined'" ) ,
) ;
} ) ;
it ( 'returns string value without modification' , ( ) = > {
const value = 'hello' ;
const expectedValue = value ;
const result = ensureType ( 'string' , value , 'myParam' ) ;
expect ( result ) . toBe ( expectedValue ) ;
} ) ;
it ( 'returns number value without modification' , ( ) = > {
const value = 42 ;
const expectedValue = value ;
const result = ensureType ( 'number' , value , 'myParam' ) ;
expect ( result ) . toBe ( expectedValue ) ;
} ) ;
it ( 'returns boolean value without modification' , ( ) = > {
const value = true ;
const expectedValue = value ;
const result = ensureType ( 'boolean' , value , 'myParam' ) ;
expect ( result ) . toBe ( expectedValue ) ;
} ) ;
it ( 'converts object to string if toType is string' , ( ) = > {
const value = { name : 'John' } ;
const expectedValue = JSON . stringify ( value ) ;
const result = ensureType ( 'string' , value , 'myParam' ) ;
expect ( result ) . toBe ( expectedValue ) ;
} ) ;
it ( 'converts string to number if toType is number' , ( ) = > {
const value = '10' ;
const expectedValue = 10 ;
const result = ensureType ( 'number' , value , 'myParam' ) ;
expect ( result ) . toBe ( expectedValue ) ;
} ) ;
it ( 'throws error for invalid conversion to number' , ( ) = > {
const value = 'invalid' ;
expect ( ( ) = > ensureType ( 'number' , value , 'myParam' ) ) . toThrowError (
new ExpressionError ( "Parameter 'myParam' must be a number, but we got 'invalid'" ) ,
) ;
} ) ;
it ( 'parses valid JSON string to object if toType is object' , ( ) = > {
const value = '{"name": "Alice"}' ;
const expectedValue = JSON . parse ( value ) ;
const result = ensureType ( 'object' , value , 'myParam' ) ;
expect ( result ) . toEqual ( expectedValue ) ;
} ) ;
it ( 'throws error for invalid JSON string to object conversion' , ( ) = > {
const value = 'invalid_json' ;
expect ( ( ) = > ensureType ( 'object' , value , 'myParam' ) ) . toThrowError (
new ExpressionError ( "Parameter 'myParam' could not be parsed" ) ,
) ;
} ) ;
it ( 'throws error for non-array value if toType is array' , ( ) = > {
const value = { name : 'Alice' } ;
expect ( ( ) = > ensureType ( 'array' , value , 'myParam' ) ) . toThrowError (
new ExpressionError ( "Parameter 'myParam' must be an array, but we got object" ) ,
) ;
} ) ;
} ) ;
2022-09-11 07:42:09 -07:00
} ) ;
2024-08-09 07:40:50 -07:00
describe ( 'isFilePathBlocked' , ( ) = > {
test ( 'should return true for static cache dir' , ( ) = > {
const filePath = Container . get ( InstanceSettings ) . staticCacheDir ;
expect ( isFilePathBlocked ( filePath ) ) . toBe ( true ) ;
} ) ;
} ) ;