diff --git a/.github/workflows/common-ci.yml b/.github/workflows/common-ci.yml index 92f51d18..d240364b 100644 --- a/.github/workflows/common-ci.yml +++ b/.github/workflows/common-ci.yml @@ -15,6 +15,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: '20.x' + registry-url: "https://registry.npmjs.org" - name: Install Packages run: npm install diff --git a/.github/workflows/common-release.yml b/.github/workflows/common-release.yml index 598fc39c..c7ec0dd1 100644 --- a/.github/workflows/common-release.yml +++ b/.github/workflows/common-release.yml @@ -19,7 +19,6 @@ jobs: - uses: actions/setup-node@v1 with: node-version: '20.x' - registry-url: "https://registry.npmjs.org" - name: Install Packages run: npm install diff --git a/jest.config.js b/jest.config.js index 3dd7929f..924b5157 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,7 +11,6 @@ module.exports = { "src/.*/model/request", "src/.*/model/response", "src/.*/model/options", - "src/utils/validations", ], testEnvironmentOptions: { "url": "https://skyflow-test.com" diff --git a/package.json b/package.json index d18894c9..898e437b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "skyflow-node", - "version": "2.2.1-beta.1", + "version": "2.2.1-beta.1-dev.92e32df", "description": "Skyflow SDK for Node.js", "main": "./lib/index.js", "module": "./lib/index.js", diff --git a/src/ _generated_/rest/api/resources/records/client/Client.ts b/src/ _generated_/rest/api/resources/records/client/Client.ts index c0682f3f..a5f28f51 100644 --- a/src/ _generated_/rest/api/resources/records/client/Client.ts +++ b/src/ _generated_/rest/api/resources/records/client/Client.ts @@ -850,9 +850,12 @@ export class Records { requestOptions?: Records.RequestOptions, ): Promise> { const _request = await core.newFormData(); - if (file != null && request.columnName != null) { - await _request.appendFile(request.columnName, file); - + if (file != null) { + await _request.appendFile("file", file); + } + + if (request.columnName != null) { + _request.append("columnName", request.columnName); } const _maybeEncodedRequest = await _request.getRequest(); @@ -1104,6 +1107,124 @@ export class Records { } } + /** + * Uploads the specified file to a record. If an existing record isn't specified, creates a new record and uploads the file to that record. + * + * @param {File | fs.ReadStream | Blob} file + * @param {string} vaultId + * @param {Skyflow.UploadFileV2Request} request + * @param {Records.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.records.uploadFileV2(fs.createReadStream("/path/to/your/file"), "d4410ea01d83473ca09a24c6b03096d4", { + * tableName: "tableName", + * columnName: "columnName" + * }) + */ + public uploadFileV2( + file: File | fs.ReadStream | Blob, + vaultId: string, + request: Skyflow.UploadFileV2Request, + requestOptions?: Records.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadFileV2(file, vaultId, request, requestOptions)); + } + + private async __uploadFileV2( + file: File | fs.ReadStream | Blob, + vaultId: string, + request: Skyflow.UploadFileV2Request, + requestOptions?: Records.RequestOptions, + ): Promise> { + const _request = await core.newFormData(); + _request.append("tableName", request.tableName); + _request.append("columnName", request.columnName); + await _request.appendFile("file", file); + if (request.skyflowID != null) { + _request.append("skyflowID", request.skyflowID); + } + + if (request.returnFileMetadata != null) { + _request.append("returnFileMetadata", request.returnFileMetadata.toString()); + } + + const _maybeEncodedRequest = await _request.getRequest(); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.SkyflowEnvironment.Production, + `v2/vaults/${encodeURIComponent(vaultId)}/files/upload`, + ), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "skyflow", + "X-Fern-SDK-Version": "1.0.21", + "User-Agent": "skyflow/1.0.21", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + ..._maybeEncodedRequest.headers, + ...requestOptions?.headers, + }, + requestType: "file", + duplex: _maybeEncodedRequest.duplex, + body: _maybeEncodedRequest.body, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as Skyflow.UploadFileV2Response, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Skyflow.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Skyflow.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + default: + throw new errors.SkyflowError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SkyflowError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.SkyflowTimeoutError( + "Timeout exceeded when calling POST /v2/vaults/{vaultID}/files/upload.", + ); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + protected async _getAuthorizationHeader(): Promise { return `Bearer ${await core.Supplier.get(this._options.token)}`; } diff --git a/src/ _generated_/rest/api/resources/records/client/requests/UploadFileV2Request.ts b/src/ _generated_/rest/api/resources/records/client/requests/UploadFileV2Request.ts new file mode 100644 index 00000000..d514b3cf --- /dev/null +++ b/src/ _generated_/rest/api/resources/records/client/requests/UploadFileV2Request.ts @@ -0,0 +1,21 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * tableName: "tableName", + * columnName: "columnName" + * } + */ +export interface UploadFileV2Request { + /** Name of the table to upload the file to. */ + tableName: string; + /** Name of the column to upload the file to. The column must have a `file` data type. */ + columnName: string; + /** Skyflow ID of the record to upload the file to. If `skyflowID` isn't specified, a new record will be created. */ + skyflowID?: string; + /** If `true`, returns metadata about the uploaded file. */ + returnFileMetadata?: boolean; +} diff --git a/src/ _generated_/rest/api/resources/records/client/requests/index.ts b/src/ _generated_/rest/api/resources/records/client/requests/index.ts index 85290118..9d13c62b 100644 --- a/src/ _generated_/rest/api/resources/records/client/requests/index.ts +++ b/src/ _generated_/rest/api/resources/records/client/requests/index.ts @@ -5,3 +5,4 @@ export { type RecordServiceBulkDeleteRecordBody } from "./RecordServiceBulkDelet export { type RecordServiceGetRecordRequest } from "./RecordServiceGetRecordRequest"; export { type RecordServiceUpdateRecordBody } from "./RecordServiceUpdateRecordBody"; export { type FileServiceUploadFileRequest } from "./FileServiceUploadFileRequest"; +export { type UploadFileV2Request } from "./UploadFileV2Request"; diff --git a/src/ _generated_/rest/api/types/DeidentifyStatusResponse.ts b/src/ _generated_/rest/api/types/DeidentifyStatusResponse.ts index 8b4589a3..8a5fa2c7 100644 --- a/src/ _generated_/rest/api/types/DeidentifyStatusResponse.ts +++ b/src/ _generated_/rest/api/types/DeidentifyStatusResponse.ts @@ -13,7 +13,7 @@ export interface DeidentifyStatusResponse { /** How the input file was specified. */ output: Skyflow.DeidentifyFileOutput[]; /** How the output file is specified. */ - output_type?: Skyflow.DeidentifyStatusResponseOutputType; + output_type: Skyflow.DeidentifyStatusResponseOutputType; /** Status details about the detect run. */ message: string; /** Number of words in the processed text. */ diff --git a/src/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts b/src/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts index 5e8dfc87..b1ae3780 100644 --- a/src/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts +++ b/src/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts @@ -5,8 +5,8 @@ /** * How the output file is specified. */ -export type DeidentifyStatusResponseOutputType = "base64" | "efs_path"; +export type DeidentifyStatusResponseOutputType = "BASE64" | "UNKNOWN"; export const DeidentifyStatusResponseOutputType = { - Base64: "base64", - EfsPath: "efs_path", + Base64: "BASE64", + Unknown: "UNKNOWN", } as const; diff --git a/src/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts b/src/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts index 78e92829..ec5e8a7a 100644 --- a/src/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts +++ b/src/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts @@ -5,9 +5,10 @@ /** * Status of the detect run. */ -export type DeidentifyStatusResponseStatus = "failed" | "in_progress" | "success"; +export type DeidentifyStatusResponseStatus = "FAILED" | "IN_PROGRESS" | "SUCCESS" | "UNKNOWN"; export const DeidentifyStatusResponseStatus = { - Failed: "failed", - InProgress: "in_progress", - Success: "success", + Failed: "FAILED", + InProgress: "IN_PROGRESS", + Success: "SUCCESS", + Unknown: "UNKNOWN", } as const; diff --git a/src/ _generated_/rest/api/types/EntityType.ts b/src/ _generated_/rest/api/types/EntityType.ts index 23372a0d..8dfef853 100644 --- a/src/ _generated_/rest/api/types/EntityType.ts +++ b/src/ _generated_/rest/api/types/EntityType.ts @@ -17,8 +17,8 @@ export type EntityType = | "credit_card_expiration" | "cvv" | "date" - | "day" | "date_interval" + | "day" | "dob" | "dose" | "driver_license" @@ -60,10 +60,10 @@ export type EntityType = | "passport_number" | "password" | "phone_number" - | "project" | "physical_attribute" | "political_affiliation" | "product" + | "project" | "religion" | "routing_number" | "sexuality" @@ -88,8 +88,8 @@ export const EntityType = { CreditCardExpiration: "credit_card_expiration", Cvv: "cvv", Date: "date", - Day: "day", DateInterval: "date_interval", + Day: "day", Dob: "dob", Dose: "dose", DriverLicense: "driver_license", @@ -131,10 +131,10 @@ export const EntityType = { PassportNumber: "passport_number", Password: "password", PhoneNumber: "phone_number", - Project: "project", PhysicalAttribute: "physical_attribute", PoliticalAffiliation: "political_affiliation", Product: "product", + Project: "project", Religion: "religion", RoutingNumber: "routing_number", Sexuality: "sexuality", diff --git a/src/ _generated_/rest/api/types/ErrorString.ts b/src/ _generated_/rest/api/types/ErrorString.ts deleted file mode 100644 index 78dd85f5..00000000 --- a/src/ _generated_/rest/api/types/ErrorString.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ - -/** - * Error message. - */ -export type ErrorString = string; diff --git a/src/ _generated_/rest/api/types/ReidentifyFileResponse.ts b/src/ _generated_/rest/api/types/ReidentifyFileResponse.ts index 1b848c8c..b3d38617 100644 --- a/src/ _generated_/rest/api/types/ReidentifyFileResponse.ts +++ b/src/ _generated_/rest/api/types/ReidentifyFileResponse.ts @@ -11,6 +11,6 @@ export interface ReidentifyFileResponse { /** Status of the re-identify operation. */ status: Skyflow.ReidentifyFileResponseStatus; /** Format of the output file. */ - output_type: "BASE64"; + output_type: Skyflow.ReidentifyFileResponseOutputType; output: Skyflow.ReidentifyFileResponseOutput; } diff --git a/src/ _generated_/rest/api/types/ReidentifyFileResponseOutputType.ts b/src/ _generated_/rest/api/types/ReidentifyFileResponseOutputType.ts new file mode 100644 index 00000000..f874b5df --- /dev/null +++ b/src/ _generated_/rest/api/types/ReidentifyFileResponseOutputType.ts @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Format of the output file. + */ +export type ReidentifyFileResponseOutputType = "BASE64" | "UNKNOWN"; +export const ReidentifyFileResponseOutputType = { + Base64: "BASE64", + Unknown: "UNKNOWN", +} as const; diff --git a/src/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts b/src/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts index a087e447..c613c7c4 100644 --- a/src/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts +++ b/src/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts @@ -5,9 +5,10 @@ /** * Status of the re-identify operation. */ -export type ReidentifyFileResponseStatus = "failed" | "in_progress" | "success"; +export type ReidentifyFileResponseStatus = "FAILED" | "IN_PROGRESS" | "SUCCESS" | "UNKNOWN"; export const ReidentifyFileResponseStatus = { - Failed: "failed", - InProgress: "in_progress", - Success: "success", + Failed: "FAILED", + InProgress: "IN_PROGRESS", + Success: "SUCCESS", + Unknown: "UNKNOWN", } as const; diff --git a/src/ _generated_/rest/api/types/ReidentifyStringResponse.ts b/src/ _generated_/rest/api/types/ReidentifyStringResponse.ts index a1d61ad4..c4e2dead 100644 --- a/src/ _generated_/rest/api/types/ReidentifyStringResponse.ts +++ b/src/ _generated_/rest/api/types/ReidentifyStringResponse.ts @@ -7,5 +7,5 @@ */ export interface ReidentifyStringResponse { /** Re-identified text. */ - processed_text?: string; + text?: string; } diff --git a/src/ _generated_/rest/api/types/UploadFileV2Response.ts b/src/ _generated_/rest/api/types/UploadFileV2Response.ts new file mode 100644 index 00000000..3a20c7b6 --- /dev/null +++ b/src/ _generated_/rest/api/types/UploadFileV2Response.ts @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Response schema for uploading a file, optionally creating a new record. + */ +export interface UploadFileV2Response { + /** Skyflow ID of the record the file was uploaded to. */ + skyflowID?: string; + fileMetadata?: unknown; +} diff --git a/src/ _generated_/rest/api/types/index.ts b/src/ _generated_/rest/api/types/index.ts index c69a1321..583a3b80 100644 --- a/src/ _generated_/rest/api/types/index.ts +++ b/src/ _generated_/rest/api/types/index.ts @@ -1,3 +1,4 @@ +export * from "./UploadFileV2Response"; export * from "./AuditEventAuditResourceType"; export * from "./AuditEventContext"; export * from "./AuditEventData"; @@ -44,10 +45,10 @@ export * from "./V1VaultSchemaConfig"; export * from "./CheckGuardrailsResponseValidation"; export * from "./CheckGuardrailsResponse"; export * from "./EntityType"; -export * from "./ErrorString"; export * from "./ErrorResponseError"; export * from "./ErrorResponse"; export * from "./ReidentifyFileResponseStatus"; +export * from "./ReidentifyFileResponseOutputType"; export * from "./ReidentifyFileResponseOutput"; export * from "./ReidentifyFileResponse"; export * from "./DeidentifyStatusResponseStatus"; diff --git a/src/error/codes/index.ts b/src/error/codes/index.ts index 12c78d1f..2410843c 100644 --- a/src/error/codes/index.ts +++ b/src/error/codes/index.ts @@ -196,6 +196,7 @@ const SKYFLOW_ERROR_CODE = { INVALID_ALLOW_REGEX_LIST: { http_code: 400, message: errorMessages.INVALID_ALLOW_REGEX_LIST }, INVALID_RESTRICT_REGEX_LIST: { http_code: 400, message: errorMessages.INVALID_RESTRICT_REGEX_LIST }, INVALID_TOKEN_FORMAT: { http_code: 400, message: errorMessages.INVALID_TOKEN_FORMAT }, + TOKEN_FORMAT_NOT_ALLOWED: { http_code: 400, message: errorMessages.VAULT_TOKEN_FORMAT_NOT_ALLOWED_FOR_DEIDENTIFY_FILES}, INVALID_TRANSFORMATIONS: { http_code: 400, message: errorMessages.INVALID_TRANSFORMATIONS }, INVALID_TEXT_IN_REIDENTIFY: { http_code: 400, message: errorMessages.INVALID_TEXT_IN_REIDENTIFY }, diff --git a/src/error/messages/index.ts b/src/error/messages/index.ts index d2e7d2a3..a60465ea 100644 --- a/src/error/messages/index.ts +++ b/src/error/messages/index.ts @@ -203,6 +203,7 @@ const errorMessages = { INVALID_RESTRICT_REGEX_LIST: `${errorPrefix} Validation error. The restrictRegexList field must be an array of strings. Specify a valid restrictRegexList.`, INVALID_TOKEN_FORMAT: `${errorPrefix} Validation error. The tokenFormat key must be an instance of TokenFormat. Specify a valid token format.`, INVALID_TRANSFORMATIONS: `${errorPrefix} Validation error. The transformations key must be an instance of Transformations. Specify a valid transformations.`, + VAULT_TOKEN_FORMAT_NOT_ALLOWED_FOR_DEIDENTIFY_FILES: `${errorPrefix} Validation error. Vault token format is not allowed for deidentify file request.`, INVALID_TEXT_IN_REIDENTIFY: `${errorPrefix} Validation error. The text field is required and must be a non-empty string. Specify a valid text.`, INVALID_REDACTED_ENTITIES_IN_REIDENTIFY: `${errorPrefix} Validation error. The redactedEntities field must be an array of DetectEntities enums. Specify a valid redactedEntities.`, diff --git a/src/utils/validations/index.ts b/src/utils/validations/index.ts index f365ab6a..f4d91a75 100644 --- a/src/utils/validations/index.ts +++ b/src/utils/validations/index.ts @@ -539,12 +539,12 @@ export const validateTokensForInsertRequest = ( export const validateInsertRequest = (insertRequest: InsertRequest, insertOptions?: InsertOptions, logLevel: LogLevel = LogLevel.ERROR) => { // if (insertRequest) { - if (!insertRequest?.tableName || !Object.prototype.hasOwnProperty.call(insertRequest, '_tableName')) { + if (!insertRequest?.table || !Object.prototype.hasOwnProperty.call(insertRequest, '_table')) { printLog(logs.errorLogs.EMPTY_TABLE_IN_INSERT, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); } - if (typeof insertRequest.tableName !== 'string' || insertRequest.tableName.trim().length === 0) { + if (typeof insertRequest.table !== 'string' || insertRequest.table.trim().length === 0) { printLog(logs.errorLogs.INVALID_TABLE_IN_INSERT, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); } @@ -600,12 +600,12 @@ export const validateUpdateOptions = (updateOptions?: UpdateOptions) => { export const validateUpdateRequest = (updateRequest: UpdateRequest, updateOptions?: UpdateOptions, logLevel: LogLevel = LogLevel.ERROR) => { if (updateRequest) { - if (!updateRequest?.tableName || !Object.prototype.hasOwnProperty.call(updateRequest, '_tableName')) { + if (!updateRequest?.table || !Object.prototype.hasOwnProperty.call(updateRequest, '_table')) { printLog(logs.errorLogs.EMPTY_TABLE_IN_UPDATE, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); } - if (typeof updateRequest.tableName !== 'string' || updateRequest.tableName.trim().length === 0) { + if (typeof updateRequest.table !== 'string' || updateRequest.table.trim().length === 0) { printLog(logs.errorLogs.INVALID_TABLE_IN_UPDATE, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); } @@ -698,12 +698,12 @@ export const validateGetOptions = (getOptions?: GetOptions) => { export const validateGetRequest = (getRequest: GetRequest, getOptions?: GetOptions, logLevel: LogLevel = LogLevel.ERROR) => { if (getRequest) { - if (!getRequest?.tableName || !Object.prototype.hasOwnProperty.call(getRequest, '_tableName')) { + if (!getRequest?.table || !Object.prototype.hasOwnProperty.call(getRequest, '_table')) { printLog(logs.errorLogs.EMPTY_TABLE_IN_GET, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); } - if (typeof getRequest.tableName !== 'string' || getRequest.tableName.trim().length === 0) { + if (typeof getRequest.table !== 'string' || getRequest.table.trim().length === 0) { printLog(logs.errorLogs.INVALID_TABLE_IN_GET, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); } @@ -738,12 +738,12 @@ export const validateGetRequest = (getRequest: GetRequest, getOptions?: GetOptio export const validateGetColumnRequest = (getRequest: GetColumnRequest, getOptions?: GetOptions, logLevel: LogLevel = LogLevel.ERROR) => { if (getRequest) { - if (!getRequest?.tableName || !Object.prototype.hasOwnProperty.call(getRequest, '_tableName')) { + if (!getRequest?.table || !Object.prototype.hasOwnProperty.call(getRequest, '_table')) { printLog(logs.errorLogs.EMPTY_TABLE_IN_GET_COLUMN, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); } - if (typeof getRequest.tableName !== 'string' || getRequest.tableName.trim().length === 0) { + if (typeof getRequest.table !== 'string' || getRequest.table.trim().length === 0) { printLog(logs.errorLogs.INVALID_TABLE_IN_GET_COLUMN, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); } @@ -883,12 +883,12 @@ export const validateTokenizeRequest = (tokenizeRequest: TokenizeRequest, logLev export const validateDeleteRequest = (deleteRequest: DeleteRequest, logLevel: LogLevel = LogLevel.ERROR) => { if (deleteRequest) { - if (!deleteRequest?.tableName || !Object.prototype.hasOwnProperty.call(deleteRequest, '_tableName')) { + if (!deleteRequest?.table || !Object.prototype.hasOwnProperty.call(deleteRequest, '_table')) { printLog(logs.errorLogs.EMPTY_TABLE_IN_DELETE, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); } - if (typeof deleteRequest?.tableName !== 'string' || deleteRequest?.tableName.trim().length === 0) { + if (typeof deleteRequest?.table !== 'string' || deleteRequest?.table.trim().length === 0) { printLog(logs.errorLogs.INVALID_TABLE_IN_DELETE, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); } @@ -926,12 +926,12 @@ export const validateUploadFileRequest = (fileRequest: FileUploadRequest, option throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST); } - if (!fileRequest?.tableName || !Object.prototype.hasOwnProperty.call(fileRequest, '_tableName')) { + if (!fileRequest?.table || !Object.prototype.hasOwnProperty.call(fileRequest, '_table')) { printLog(logs.errorLogs.EMPTY_TABLE_IN_FILE_UPLOAD, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.MISSING_TABLE_IN_UPLOAD_FILE); } - if (typeof fileRequest?.tableName !== 'string' || fileRequest?.tableName.trim().length === 0) { + if (typeof fileRequest?.table !== 'string' || fileRequest?.table.trim().length === 0) { printLog(logs.errorLogs.INVALID_TABLE_IN_FILE_UPLOAD, MessageType.ERROR, logLevel); throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TABLE_IN_UPLOAD_FILE); } @@ -1152,6 +1152,11 @@ export const validateDeidentifyFileOptions = (deidentifyFileOptions: DeidentifyF throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TOKEN_FORMAT); } + const tokenFormat = deidentifyFileOptions.getTokenFormat(); + if(tokenFormat != null && tokenFormat.getVaultToken() != null && tokenFormat.getVaultToken()!.length > 0) { + throw new SkyflowError(SKYFLOW_ERROR_CODE.TOKEN_FORMAT_NOT_ALLOWED); + } + // Validate transformations if (deidentifyFileOptions.getTransformations() && !(deidentifyFileOptions.getTransformations() instanceof Transformations)) { throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TRANSFORMATIONS); diff --git a/src/vault/controller/detect/index.ts b/src/vault/controller/detect/index.ts index a1914201..83672c11 100644 --- a/src/vault/controller/detect/index.ts +++ b/src/vault/controller/detect/index.ts @@ -202,7 +202,6 @@ class DetectController { } as TokenTypeWithoutVault, allow_regex: options?.getAllowRegexList(), restrict_regex: options?.getRestrictRegexList(), - transformations: this.getTransformations(options) as GeneratedTransformations, }; return spreadsheetRequest; } @@ -369,7 +368,7 @@ class DetectController { }, waitTime * 1000); } } else if (response.status?.toUpperCase() === 'SUCCESS') { - resolve(response); // Resolve with the processed file response + resolve([response, runId]); // Resolve with the processed file response and runId } else if (response.status?.toUpperCase() === 'FAILED') { reject(new SkyflowError(SKYFLOW_ERROR_CODE.INTERNAL_SERVER_ERROR, [response.message])); @@ -475,7 +474,7 @@ class DetectController { return new DeidentifyFileResponse({ fileBase64: base64String, file: file, - type: data.output?.[0]?.processedFileType ?? '', + type: data.output?.[0]?.processedFileType ?? data.outputType ?? "", extension: extension, wordCount: data.wordCharacterCount?.wordCount ?? 0, charCount: data.wordCharacterCount?.characterCount ?? 0, @@ -606,7 +605,7 @@ class DetectController { this.waitTime = options?.getWaitTime() ?? this.waitTime; var reqType : DeidenitfyFileRequestTypes = this.getReqType(fileExtension); - var promiseReq: Promise; + var promiseReq: Promise<[DeidentifyFileDetectRunResponse, string]>; switch (reqType){ case DeidenitfyFileRequestTypes.AUDIO: promiseReq = this.buildAudioRequest(fileObj, options, fileExtension) @@ -709,17 +708,17 @@ class DetectController { break; } - promiseReq.then(data => { - if(data.runId){ + promiseReq.then(([data, runId]) => { + if(runId && data.status === "IN_PROGRESS") { resolve(new DeidentifyFileResponse({ - runId: data.runId, - status: 'IN_PROGRESS', + runId: runId, + status: data.status, })); } if (options?.getOutputDirectory() && data.status === "SUCCESS") { this.processDeidentifyFileResponse(data, options.getOutputDirectory() as string, fileBaseName); } - const deidentifiedFileResponse = this.parseDeidentifyFileResponse(data, undefined, data.status); + const deidentifiedFileResponse = this.parseDeidentifyFileResponse(data, runId, data.status); resolve(deidentifiedFileResponse); }).catch(error => { reject(error) diff --git a/src/vault/controller/vault/index.ts b/src/vault/controller/vault/index.ts index 797b5de1..eb6aecc6 100644 --- a/src/vault/controller/vault/index.ts +++ b/src/vault/controller/vault/index.ts @@ -1,7 +1,7 @@ //imports import * as fs from 'fs'; import InsertRequest from "../../model/request/insert"; -import { BatchRecordMethod, QueryServiceExecuteQueryBody, RecordServiceBatchOperationBody, RecordServiceBulkDeleteRecordBody, RecordServiceInsertRecordBody, RecordServiceUpdateRecordBody, V1Byot, V1DetokenizePayload, V1DetokenizeRecordRequest, V1FieldRecords, V1TokenizePayload, V1TokenizeRecordRequest } from '../../../ _generated_/rest/api'; +import { BatchRecordMethod, QueryServiceExecuteQueryBody, RecordServiceBatchOperationBody, RecordServiceBulkDeleteRecordBody, RecordServiceInsertRecordBody, RecordServiceUpdateRecordBody, UploadFileV2Request, UploadFileV2Response, V1Byot, V1DetokenizePayload, V1DetokenizeRecordRequest, V1FieldRecords, V1TokenizePayload, V1TokenizeRecordRequest } from '../../../ _generated_/rest/api'; import InsertOptions from "../../model/options/insert"; import GetRequest from "../../model/request/get"; import GetOptions from "../../model/options/get"; @@ -184,7 +184,7 @@ class VaultController { private buildBatchInsertBody(request: InsertRequest, options?: InsertOptions): RecordServiceBatchOperationBody { const records = request.data.map((record, index) => ({ fields: record as Record || {}, - tableName: request.tableName, + tableName: request.table, tokenization: options?.getReturnTokens() || false, method: BatchRecordMethod.Post, tokens: this.getTokens(index, options?.getTokens()) as Record, @@ -235,7 +235,7 @@ class VaultController { const operationType = isContinueOnError ? TYPES.INSERT_BATCH : TYPES.INSERT; - const tableName = request.tableName; + const tableName = request.table; this.handleRequest( (headers: Records.RequestOptions | undefined) => isContinueOnError @@ -281,7 +281,7 @@ class VaultController { this.handleRequest( (headers: Records.RequestOptions | undefined) => this.client.vaultAPI.recordServiceUpdateRecord( this.client.vaultId, - request.tableName, + request.table, skyflowId as string, updateData, headers @@ -321,7 +321,7 @@ class VaultController { this.handleRequest( (headers: Records.RequestOptions | undefined) => this.client.vaultAPI.recordServiceBulkDeleteRecord( this.client.vaultId, - request.tableName, + request.table, deleteRequest, headers ).withRawResponse(), @@ -382,7 +382,7 @@ class VaultController { this.handleRequest( (headers: Records.RequestOptions | undefined) => this.client.vaultAPI.recordServiceBulkGetRecord( this.client.vaultId, - request.tableName, + request.table, payload, headers ).withRawResponse(), @@ -437,20 +437,23 @@ class VaultController { fileBlob = options?.getFileObject(); } - this.handleRequest( - (headers: Records.RequestOptions | undefined) => this.client.vaultAPI.fileServiceUploadFile( + const uploadFileV2Request: UploadFileV2Request = { + columnName:request.columnName, + tableName: request.table, + skyflowID: request.skyflowId, + returnFileMetadata: false, + } + + this.handleRequest( + (headers: Records.RequestOptions | undefined) => this.client.vaultAPI.uploadFileV2( fileBlob as unknown as import('buffer').Blob, this.client.vaultId, - request.tableName, - request.skyflowId, - { - columnName: request.columnName - } + uploadFileV2Request, ).withRawResponse(), TYPES.FILE_UPLOAD ).then(data => { printLog(logs.infoLogs.FILE_UPLOAD_DATA_SUCCESS, MessageType.LOG, this.client.getLogLevel()); - resolve(new FileUploadResponse({ skyflowId: data.skyflow_id, errors: null })); + resolve(new FileUploadResponse({ skyflowId: data.skyflowID ?? "", errors: null })); }) .catch(error => { reject(error); diff --git a/src/vault/model/request/delete/index.ts b/src/vault/model/request/delete/index.ts index aaec3345..f3980922 100644 --- a/src/vault/model/request/delete/index.ts +++ b/src/vault/model/request/delete/index.ts @@ -2,23 +2,23 @@ class DeleteRequest { //fields - private _tableName: string; + private _table: string; private _ids: Array; // Constructor - constructor(tableName: string, deleteIds: Array) { - this._tableName = tableName; + constructor(table: string, deleteIds: Array) { + this._table = table; this._ids = deleteIds; } - // Getter for tableName - public get tableName(): string { - return this._tableName; + // Getter for table + public get table(): string { + return this._table; } - // Setter for tableName - public set tableName(value: string) { - this._tableName = value; + // Setter for table + public set table(value: string) { + this._table = value; } // Getter for deleteData diff --git a/src/vault/model/request/file-upload/index.ts b/src/vault/model/request/file-upload/index.ts index 74acb6ca..3c000def 100644 --- a/src/vault/model/request/file-upload/index.ts +++ b/src/vault/model/request/file-upload/index.ts @@ -1,23 +1,23 @@ // Imports class FileUploadRequest { - private _tableName: string; + private _table: string; private _skyflowId: string; private _columnName: string; // Constructor - constructor(tableName: string, skyflowId: string, columnName: string) { - this._tableName = tableName; + constructor(table: string, skyflowId: string, columnName: string) { + this._table = table; this._skyflowId = skyflowId; this._columnName = columnName; } // Getters and Setters - public get tableName(): string { - return this._tableName; + public get table(): string { + return this._table; } - public set tableName(value: string) { - this._tableName = value; + public set table(value: string) { + this._table = value; } public get skyflowId(): string { diff --git a/src/vault/model/request/get-column/index.ts b/src/vault/model/request/get-column/index.ts index 10ba453f..7c3ed3c0 100644 --- a/src/vault/model/request/get-column/index.ts +++ b/src/vault/model/request/get-column/index.ts @@ -3,25 +3,25 @@ class GetColumnRequest { //fields - private _tableName: string; + private _table: string; private _columnName: string; private _columnValues: Array; // Constructor - constructor(tableName: string, _columnName: string, _columnValues: Array) { - this._tableName = tableName; + constructor(table: string, _columnName: string, _columnValues: Array) { + this._table = table; this._columnName = _columnName; this._columnValues = _columnValues; } - // Getter for tableName - public get tableName(): string { - return this._tableName; + // Getter for table + public get table(): string { + return this._table; } - // Setter for tableName - public set tableName(value: string) { - this._tableName = value; + // Setter for table + public set table(value: string) { + this._table = value; } // Getter for columnName diff --git a/src/vault/model/request/get/index.ts b/src/vault/model/request/get/index.ts index f830fcf2..575c2009 100644 --- a/src/vault/model/request/get/index.ts +++ b/src/vault/model/request/get/index.ts @@ -5,23 +5,23 @@ import { RedactionType } from "../../../../utils"; class GetRequest { //fields - private _tableName: string; + private _table: string; private _ids: Array; // Constructor - constructor(tableName: string, _ids: Array) { - this._tableName = tableName; + constructor(table: string, _ids: Array) { + this._table = table; this._ids = _ids; } - // Getter for tableName - public get tableName(): string { - return this._tableName; + // Getter for table + public get table(): string { + return this._table; } - // Setter for tableName - public set tableName(value: string) { - this._tableName = value; + // Setter for table + public set table(value: string) { + this._table = value; } // Getter for ids diff --git a/src/vault/model/request/insert/index.ts b/src/vault/model/request/insert/index.ts index 575fcb2e..ee251c9f 100644 --- a/src/vault/model/request/insert/index.ts +++ b/src/vault/model/request/insert/index.ts @@ -3,23 +3,23 @@ class InsertRequest { //fields - private _tableName: string; + private _table: string; private _data: Record[]; // Constructor - constructor(tableName: string, data: Record[]) { - this._tableName = tableName; + constructor(table: string, data: Record[]) { + this._table = table; this._data = data; } - // Getter for tableName - public get tableName(): string { - return this._tableName; + // Getter for table + public get table(): string { + return this._table; } - // Setter for tableName - public set tableName(value: string) { - this._tableName = value; + // Setter for table + public set table(value: string) { + this._table = value; } // Getter for _data diff --git a/src/vault/model/request/update/index.ts b/src/vault/model/request/update/index.ts index 14f99dd9..a749f74c 100644 --- a/src/vault/model/request/update/index.ts +++ b/src/vault/model/request/update/index.ts @@ -3,23 +3,23 @@ class UpdateRequest { //fields - private _tableName: string; + private _table: string; private _data: Record; // Constructor - constructor(tableName: string, data: Record) { - this._tableName = tableName; + constructor(table: string, data: Record) { + this._table = table; this._data = data; } - // Getter for tableName - public get tableName(): string { - return this._tableName; + // Getter for table + public get table(): string { + return this._table; } - // Setter for tableName - public set tableName(value: string) { - this._tableName = value; + // Setter for table + public set table(value: string) { + this._table = value; } // Getter for updateData diff --git a/src/vault/types/index.ts b/src/vault/types/index.ts index 09038aa6..e68f84ef 100644 --- a/src/vault/types/index.ts +++ b/src/vault/types/index.ts @@ -198,15 +198,14 @@ export interface DeidentifyFileOutput { processedFileExtension?: string; } -export type DeidentifyStatusResponseOutputType = "BASE64" | "EFS_PATH" | "UNKNOWN"; - +export type DeidentifyStatusResponseOutputType = "BASE64" | "UNKNOWN"; export type WordCharacterCount = { wordCount?: number; characterCount?: number; } export interface DeidentifyFileDetectRunResponse { - status: "FAILED" | "IN_PROGRESS" | "SUCCESS"; + status: "FAILED" | "IN_PROGRESS" | "SUCCESS" | "UNKNOWN"; output: DeidentifyFileOutput[]; outputType: DeidentifyStatusResponseOutputType; message: string; diff --git a/test/utils/validations.test.js b/test/utils/validations.test.js new file mode 100644 index 00000000..8520152c --- /dev/null +++ b/test/utils/validations.test.js @@ -0,0 +1,4039 @@ +const fs = require('fs'); +const path = require('path'); +const { validateDeidentifyFileRequest, validateSkyflowConfig, validateVaultConfig, validateUpdateVaultConfig, validateSkyflowCredentials, validateConnectionConfig, validateUpdateConnectionConfig, validateInsertRequest, validateUpdateRequest, validateGetRequest, validateDetokenizeRequest, validateTokenizeRequest, validateDeleteRequest, validateUploadFileRequest, validateQueryRequest, validateDeIdentifyTextRequest, validateReidentifyTextRequest, validateGetDetectRunRequest, validateInvokeConnectionRequest, validateUpdateOptions, validateGetColumnRequest, validateCredentialsWithId } = require('../../src/utils/validations'); +const DeidentifyFileRequest = require('../../src/vault/model/request/deidentify-file').default; +const DeidentifyFileOptions = require('../../src/vault/model/options/deidentify-file').default; +const SKYFLOW_ERROR_CODE = require('../../src/error/codes'); +const { default: TokenFormat } = require('../../src/vault/model/options/deidentify-text/token-format'); +const { DetectEntities, Env, TokenMode, OrderByEnum } = require('../../src/utils'); +const { LogLevel } = require('../../src/utils'); +const { default: Transformations } = require('../../src/vault/model/options/deidentify-text/transformations'); + +describe('validateDeidentifyFileRequest', () => { + let mockFile; + let mockFilePath; + + beforeEach(() => { + // Create a mock File object + mockFile = new File(['test content'], 'test.txt', { type: 'text/plain' }); + mockFilePath = '/path/to/file.txt'; + jest.spyOn(fs, 'existsSync').mockImplementation(() => true); + jest.spyOn(fs, 'lstatSync').mockImplementation(() => ({ + isFile: () => true, + isDirectory: () => true + })); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should throw error when request is null', () => { + expect(() => validateDeidentifyFileRequest(null)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DEIDENTIFY_FILE_REQUEST); + }); + + test('should throw error when request is undefined', () => { + expect(() => validateDeidentifyFileRequest(undefined)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DEIDENTIFY_FILE_REQUEST); + }); + + test('should throw error when neither file nor filePath is provided', () => { + const request = new DeidentifyFileRequest({}); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DEIDENTIFY_FILE_INPUT); + }); + + test('should throw error when both file and filePath are provided', () => { + const request = new DeidentifyFileRequest({ + file: mockFile, + filePath: mockFilePath + }); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DEIDENTIFY_FILE_INPUT); + }); + + // File object validation tests + test('should validate request with valid file object', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + expect(() => validateDeidentifyFileRequest(request)).not.toThrow(); + }); + + test('should throw error for invalid file object', () => { + const request = new DeidentifyFileRequest({ + file: { invalid: 'file' } + }); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_TYPE); + }); + + test('should throw error for file with empty name', () => { + const invalidFile = new File(['content'], '', { type: 'text/plain' }); + const request = new DeidentifyFileRequest({ file: invalidFile }); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_TYPE); + }); + + test('should throw error when file is not a File instance', () => { + const request = new DeidentifyFileRequest({ + file: { name: 'test.txt', type: 'text/plain' } + }); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_TYPE); + }); + + test('should throw error when file name is invalid', () => { + const invalidFile = new File(['content'], ' ', { type: 'text/plain' }); + const request = new DeidentifyFileRequest({ file: invalidFile }); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_TYPE); + }); + + // File path validation tests + test('should validate request with valid file path', () => { + const request = new DeidentifyFileRequest({ filePath: mockFilePath }); + expect(() => validateDeidentifyFileRequest(request)).not.toThrow(); + }); + + test('should throw error for non-existent file path', () => { + jest.spyOn(fs, 'existsSync').mockImplementation(() => false); + const request = new DeidentifyFileRequest({ + filePath: '/invalid/path.txt' + }); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DEIDENTIFY_FILE_PATH); + }); + + test('should throw error when file path points to directory', () => { + jest.spyOn(fs, 'lstatSync').mockImplementation(() => ({ + isFile: () => false + })); + const request = new DeidentifyFileRequest({ + filePath: '/path/to/directory' + }); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DEIDENTIFY_FILE_PATH); + }); + + test('should throw error for empty file path', () => { + const request = new DeidentifyFileRequest({ filePath: '' }); + expect(() => validateDeidentifyFileRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DEIDENTIFY_FILE_PATH); + }); + + // Options validation tests + test('should validate request with options containing valid wait time', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setWaitTime(30); + expect(() => validateDeidentifyFileRequest(request, options)).not.toThrow(); + }); + + test('should throw error for invalid wait time', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setWaitTime(65); // More than maximum allowed + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(); + }); + + test('should validate request with valid output directory', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setOutputDirectory('/valid/output/dir'); + expect(() => validateDeidentifyFileRequest(request, options)).not.toThrow(); + }); + + test('should throw error when output directory does not exist', () => { + jest.spyOn(fs, 'existsSync').mockImplementation(() => false); + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setOutputDirectory('/nonexistent/dir'); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_OUTPUT_DIRECTORY_PATH); + }); + + test('should validate request with valid entities array', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setEntities(['PERSON', 'LOCATION']); + expect(() => validateDeidentifyFileRequest(request, options)).not.toThrow(); + }); + + test('should throw error for invalid entities type', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setEntities('PERSON'); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_ENTITIES); + }); + + test('should validate request with valid allow regex list', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setAllowRegexList(['^test', 'pattern$']); + expect(() => validateDeidentifyFileRequest(request, options)).not.toThrow(); + }); + + test('should throw error for invalid allow regex list', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setAllowRegexList('pattern'); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_ALLOW_REGEX_LIST); + }); + + test('should validate request with valid entities array', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setEntities(['PERSON', 'LOCATION']); + expect(() => validateDeidentifyFileRequest(request, options)).not.toThrow(); + }); + + test('should throw error for invalid entities type', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setEntities('PERSON'); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_ENTITIES); + }); + + test('should validate request with valid allow regex list', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setAllowRegexList(['^test', 'pattern$']); + expect(() => validateDeidentifyFileRequest(request, options)).not.toThrow(); + }); + + + + test('should throw error when options is invalid', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + expect(() => validateDeidentifyFileRequest(request, {})) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DEIDENTIFY_FILE_OPTIONS); + }); + + test('should throw error for invalid transformations', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setTransformations({}); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TRANSFORMATIONS); + }); + + test('should throw error for invalid output processed image type', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setOutputProcessedImage('true'); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_OUTPUT_PROCESSED_IMAGE); + }); + + + + test('should throw error for invalid masking method type', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setMaskingMethod(123); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_MASKING_METHOD); + }); + + test('should throw error for invalid pixel density type', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setPixelDensity('300'); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_PIXEL_DENSITY); + }); + + test('should throw error for invalid max resolution type', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setMaxResolution('1024'); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_MAX_RESOLUTION); + }); + + test('should throw error for invalid output processed audio type', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setOutputProcessedAudio('true'); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_OUTPUT_PROCESSED_AUDIO); + }); + + test('should throw error for invalid output transcription type', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setOutputTranscription(123); + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_OUTPUT_TRANSCRIPTION); + }); + + test("should throw error in case of vault token in deidentify file request", () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + const tokenFormat = new TokenFormat(); + tokenFormat.setVaultToken(DetectEntities.CREDIT_CARD); + options.setTokenFormat(tokenFormat); + + expect(() => validateDeidentifyFileRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_VAULT_TOKEN); + }); + + test('should validate request with all valid parameters', () => { + const request = new DeidentifyFileRequest({ file: mockFile }); + const options = new DeidentifyFileOptions(); + options.setWaitTime(30); + options.setOutputDirectory('/valid/output/dir'); + options.setEntities(['PERSON', 'LOCATION']); + options.setAllowRegexList(['^test', 'pattern$']); + expect(() => validateDeidentifyFileRequest(request, options)).not.toThrow(); + }); +}); + +describe('validateSkyflowConfig', () => { + // Test for null/undefined config + test('should throw error when config is null/undefined', () => { + expect(() => validateSkyflowConfig(null)).toThrow(SKYFLOW_ERROR_CODE.CONFIG_MISSING); + expect(() => validateSkyflowConfig(undefined)).toThrow(SKYFLOW_ERROR_CODE.CONFIG_MISSING); + }); + + // Test for missing both vaultConfigs and connectionConfigs + test('should throw error when both vaultConfigs and connectionConfigs are missing', () => { + const config = {}; + expect(() => validateSkyflowConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_SKYFLOW_CONFIG); + }); + + // Test for invalid vaultConfigs type + test('should throw error when vaultConfigs is not an array', () => { + const config = { + vaultConfigs: {} // Not an array + }; + expect(() => validateSkyflowConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TYPE_FOR_CONFIG); + }); + + // Test for invalid connectionConfigs type + test('should throw error when connectionConfigs is not an array', () => { + const config = { + connectionConfigs: {} // Not an array + }; + expect(() => validateSkyflowConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TYPE_FOR_CONFIG); + }); + + // Test for valid config with vaultConfigs only + test('should validate config with valid vaultConfigs array', () => { + const config = { + vaultConfigs: [] + }; + expect(() => validateSkyflowConfig(config)).not.toThrow(); + }); + + // Test for valid config with connectionConfigs only + test('should validate config with valid connectionConfigs array', () => { + const config = { + connectionConfigs: [] + }; + expect(() => validateSkyflowConfig(config)).not.toThrow(); + }); + + // Test for valid config with both configs + test('should validate config with both valid vaultConfigs and connectionConfigs', () => { + const config = { + vaultConfigs: [], + connectionConfigs: [] + }; + expect(() => validateSkyflowConfig(config)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const config = { + vaultConfigs: [] + }; + expect(() => validateSkyflowConfig(config, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateSkyflowConfig(config, LogLevel.INFO)).not.toThrow(); + expect(() => validateSkyflowConfig(config, LogLevel.WARN)).not.toThrow(); + expect(() => validateSkyflowConfig(config, LogLevel.ERROR)).not.toThrow(); + }); + + // Test with empty arrays + test('should accept empty arrays for both configs', () => { + const config = { + vaultConfigs: [], + connectionConfigs: [] + }; + expect(() => validateSkyflowConfig(config)).not.toThrow(); + }); + + // Test with null arrays + test('should throw error when arrays are null', () => { + const config = { + vaultConfigs: null, + connectionConfigs: null + }; + expect(() => validateSkyflowConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TYPE_FOR_CONFIG); + }); + + // Test with undefined arrays + test('should handle undefined arrays gracefully', () => { + const config = { + vaultConfigs: undefined, + connectionConfigs: undefined + }; + expect(() => validateSkyflowConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_SKYFLOW_CONFIG); + }); +}); + +describe('validateVaultConfig', () => { + // Test for null/undefined config + test('should throw error when vaultConfig is null/undefined', () => { + expect(() => validateVaultConfig(null)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_VAULT_CONFIG); + expect(() => validateVaultConfig(undefined)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_VAULT_CONFIG); + }); + + // Test for missing vaultId + test('should throw error when vaultId is missing', () => { + const config = { + clusterId: 'test-cluster' + }; + expect(() => validateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_VAULT_ID); + }); + + // Test for invalid vaultId + test('should throw error when vaultId is invalid', () => { + const config = { + vaultId: '', // empty string + clusterId: 'test-cluster' + }; + expect(() => validateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VAULT_ID); + + const config2 = { + vaultId: 123, // not a string + clusterId: 'test-cluster' + }; + expect(() => validateVaultConfig(config2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VAULT_ID); + }); + + // Test for missing clusterId + test('should throw error when clusterId is missing', () => { + const config = { + vaultId: 'test-vault' + }; + expect(() => validateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_CLUSTER_ID); + }); + + // Test for invalid clusterId + test('should throw error when clusterId is invalid', () => { + const config = { + vaultId: 'test-vault', + clusterId: '' // empty string + }; + expect(() => validateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CLUSTER_ID); + + const config2 = { + vaultId: 'test-vault', + clusterId: 123 // not a string + }; + expect(() => validateVaultConfig(config2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CLUSTER_ID); + }); + + // Test for invalid env + test('should throw error when env is invalid', () => { + const config = { + vaultId: 'test-vault', + clusterId: 'test-cluster', + env: 'INVALID_ENV' + }; + expect(() => validateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ENV); + }); + + // Test for valid env values + test('should accept valid env values', () => { + const config = { + vaultId: 'test-vault', + clusterId: 'test-cluster', + env: Env.PROD + }; + expect(() => validateVaultConfig(config)).not.toThrow(); + }); + + // Test for valid configuration + test('should accept valid vault configuration', () => { + const config = { + vaultId: 'test-vault', + clusterId: 'test-cluster', + env: Env.PROD, + credentials: { + apiKey: 'sky-key-123' // Valid API key format + } + }; + expect(() => validateVaultConfig(config)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const config = { + vaultId: 'test-vault', + clusterId: 'test-cluster' + }; + expect(() => validateVaultConfig(config, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateVaultConfig(config, LogLevel.INFO)).not.toThrow(); + expect(() => validateVaultConfig(config, LogLevel.WARN)).not.toThrow(); + expect(() => validateVaultConfig(config, LogLevel.ERROR)).not.toThrow(); + }); + + // Test for empty strings + test('should throw error for empty string values', () => { + const config = { + vaultId: '', // whitespace only + clusterId: 'test-cluster' + }; + expect(() => validateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VAULT_ID); + }); + + // Test for proper error messages + test('should throw errors with proper error codes', () => { + const config = { + vaultId: 'test-vault', + clusterId: '' + }; + try { + validateVaultConfig(config); + } catch (error) { + expect(error.code).toBe(SKYFLOW_ERROR_CODE.INVALID_CLUSTER_ID); + } + }); +}); + +describe('validateUpdateVaultConfig', () => { + // Test for null/undefined config + test('should throw error when vaultConfig is null/undefined', () => { + expect(() => validateUpdateVaultConfig(null)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_VAULT_CONFIG); + expect(() => validateUpdateVaultConfig(undefined)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_VAULT_CONFIG); + }); + + // Test for missing vaultId + test('should throw error when vaultId is missing', () => { + const config = { + clusterId: 'test-cluster', + env: Env.PROD + }; + expect(() => validateUpdateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_VAULT_ID); + }); + + // Test for invalid vaultId + test('should throw error when vaultId is invalid', () => { + const config = { + vaultId: '', // empty string + clusterId: 'test-cluster' + }; + expect(() => validateUpdateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VAULT_ID); + + const config2 = { + vaultId: 123, // not a string + clusterId: 'test-cluster' + }; + expect(() => validateUpdateVaultConfig(config2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VAULT_ID); + }); + + // Test for invalid clusterId type + test('should throw error when clusterId is invalid type', () => { + const config = { + vaultId: 'test-vault', + clusterId: 123 // not a string + }; + expect(() => validateUpdateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CLUSTER_ID); + }); + + // Test for invalid env + test('should throw error when env is invalid', () => { + const config = { + vaultId: 'test-vault', + env: 'INVALID_ENV' + }; + expect(() => validateUpdateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ENV); + }); + + // Test for credentials validation + test('should validate credentials if provided', () => { + const config = { + vaultId: 'test-vault', + credentials: { + apiKey: 'invalid-key' // Invalid API key format + } + }; + expect(() => validateUpdateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_API_KEY_WITH_ID); + }); + + // Test for valid partial update with only vaultId + test('should accept valid config with only vaultId', () => { + const config = { + vaultId: 'test-vault' + }; + expect(() => validateUpdateVaultConfig(config)).not.toThrow(); + }); + + // Test for valid partial update with some fields + test('should accept valid partial vault configuration', () => { + const config = { + vaultId: 'test-vault', + clusterId: 'test-cluster' + }; + expect(() => validateUpdateVaultConfig(config)).not.toThrow(); + }); + + // Test for valid full update + test('should accept valid full vault configuration', () => { + const config = { + vaultId: 'test-vault', + clusterId: 'test-cluster', + env: Env.PROD, + credentials: { + apiKey: 'sky-key-123' // Valid API key format + } + }; + expect(() => validateUpdateVaultConfig(config)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const config = { + vaultId: 'test-vault' + }; + expect(() => validateUpdateVaultConfig(config, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateUpdateVaultConfig(config, LogLevel.INFO)).not.toThrow(); + expect(() => validateUpdateVaultConfig(config, LogLevel.WARN)).not.toThrow(); + expect(() => validateUpdateVaultConfig(config, LogLevel.ERROR)).not.toThrow(); + }); + + // Test for whitespace values + test('should throw error for whitespace string values', () => { + const config = { + vaultId: '', // whitespace only + }; + expect(() => validateUpdateVaultConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VAULT_ID); + }); +}); + +describe('validateSkyflowCredentials', () => { + // Test for null/undefined credentials + test('should throw error when credentials are null/undefined', () => { + expect(() => validateSkyflowCredentials(null)).toThrow(SKYFLOW_ERROR_CODE.CREDENTIALS_WITH_NO_VALID_KEY); + expect(() => validateSkyflowCredentials(undefined)).toThrow(SKYFLOW_ERROR_CODE.CREDENTIALS_WITH_NO_VALID_KEY); + }); + + // Test for empty credentials object + test('should throw error when credentials object is empty', () => { + expect(() => validateSkyflowCredentials({})).toThrow(SKYFLOW_ERROR_CODE.CREDENTIALS_WITH_NO_VALID_KEY); + }); + + // Test for multiple credential types + test('should throw error when multiple credential types are provided', () => { + const credentials = { + token: 'valid-token', + apiKey: 'sky-key-123' + }; + expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.MULTIPLE_CREDENTIALS_PASSED); + }); + + // Test TokenCredentials validation + describe('TokenCredentials validation', () => { + test('should accept valid token credentials', () => { + const credentials = { + token: 'valid-non-expired-token' + }; + jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(false); + expect(() => validateSkyflowCredentials(credentials)).not.toThrow(); + }); + + test('should throw error for expired token', () => { + const credentials = { + token: 'expired-token' + }; + jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(true); + expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.INVALID_BEARER_TOKEN); + }); + }); + + // Test PathCredentials validation + describe('PathCredentials validation', () => { + test('should accept valid path credentials', () => { + const credentials = { + path: '/valid/path/to/file' + }; + jest.spyOn(require('fs'), 'existsSync').mockReturnValue(true); + expect(() => validateSkyflowCredentials(credentials)).not.toThrow(); + }); + + test('should throw error for invalid path', () => { + const credentials = { + path: '' + }; + expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CREDENTIALS_FILE_PATH); + }); + + test('should validate roles if provided', () => { + const credentials = { + path: '/valid/path', + roles: 'invalid-roles' // should be array + }; + jest.spyOn(require('fs'), 'existsSync').mockReturnValue(true); + expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ROLES_KEY_TYPE); + }); + + test('should validate context if provided', () => { + const credentials = { + path: '/valid/path', + context: 123 // should be string + }; + jest.spyOn(require('fs'), 'existsSync').mockReturnValue(true); + expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONTEXT); + }); + }); + + // Test StringCredentials validation + describe('StringCredentials validation', () => { + test('should accept valid string credentials', () => { + const credentials = { + credentialsString: JSON.stringify({ + clientID: 'test', + keyID: 'test', + privateKey: 'test' + }) + }; + expect(() => validateSkyflowCredentials(credentials)).not.toThrow(); + }); + + test('should throw error for invalid credentials string', () => { + const credentials = { + credentialsString: 'invalid-json' + }; + expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.INVALID_PARSED_CREDENTIALS_STRING); + }); + + test('should validate roles if provided', () => { + const credentials = { + credentialsString: JSON.stringify({ + clientID: 'test', + keyID: 'test' + }), + roles: 'invalid-roles' // should be array + }; + expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ROLES_KEY_TYPE); + }); + }); + + // Test ApiKeyCredentials validation + describe('ApiKeyCredentials validation', () => { + test('should accept valid API key credentials', () => { + const credentials = { + apiKey: 'sky-key-123' + }; + expect(() => validateSkyflowCredentials(credentials)).not.toThrow(); + }); + + test('should throw error for invalid API key format', () => { + const credentials = { + apiKey: 'invalid-key' + }; + expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.INVALID_API_KEY); + }); + }); + + // Test different log levels + test('should work with different log levels', () => { + const credentials = { + apiKey: 'sky-key-123' + }; + expect(() => validateSkyflowCredentials(credentials, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateSkyflowCredentials(credentials, LogLevel.INFO)).not.toThrow(); + expect(() => validateSkyflowCredentials(credentials, LogLevel.WARN)).not.toThrow(); + expect(() => validateSkyflowCredentials(credentials, LogLevel.ERROR)).not.toThrow(); + }); +}); + + +describe('validateConnectionConfig', () => { + // Test for null/undefined config + test('should throw error when connectionConfig is null/undefined', () => { + expect(() => validateConnectionConfig(null)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_CONFIG); + expect(() => validateConnectionConfig(undefined)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_CONFIG); + }); + + // Test for missing connectionId + test('should throw error when connectionId is missing', () => { + const config = { + connectionUrl: 'https://example.com' + }; + expect(() => validateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_ID); + }); + + // Test for invalid connectionId + test('should throw error when connectionId is invalid', () => { + const config = { + connectionId: '', // empty string + connectionUrl: 'https://example.com' + }; + expect(() => validateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_ID); + + const config2 = { + connectionId: 123, // not a string + connectionUrl: 'https://example.com' + }; + expect(() => validateConnectionConfig(config2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_ID); + }); + + // Test for missing connectionUrl + test('should throw error when connectionUrl is missing', () => { + const config = { + connectionId: 'test-connection' + }; + expect(() => validateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_URL); + }); + + // Test for invalid connectionUrl + test('should throw error when connectionUrl is invalid', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: '' // empty string + }; + expect(() => validateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_URL); + + const config2 = { + connectionId: 'test-connection', + connectionUrl: 'not-a-url' // invalid URL format + }; + expect(() => validateConnectionConfig(config2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_URL); + }); + + // Test for invalid URL format + test('should throw error for invalid URL format', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'invalid-url-format' + }; + expect(() => validateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_URL); + }); + + // Test credentials validation + test('should validate credentials if provided', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'https://example.com', + credentials: { + apiKey: 'invalid-key' // Invalid API key format + } + }; + expect(() => validateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_API_KEY_WITH_ID); + }); + + // Test for valid configuration + test('should accept valid connection configuration', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'https://example.com', + credentials: { + apiKey: 'sky-key-123' // Valid API key format + } + }; + expect(() => validateConnectionConfig(config)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'https://example.com' + }; + expect(() => validateConnectionConfig(config, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateConnectionConfig(config, LogLevel.INFO)).not.toThrow(); + expect(() => validateConnectionConfig(config, LogLevel.WARN)).not.toThrow(); + expect(() => validateConnectionConfig(config, LogLevel.ERROR)).not.toThrow(); + }); + + // Test for whitespace values + test('should throw error for whitespace string values', () => { + const config = { + connectionId: ' ', // whitespace only + connectionUrl: 'https://example.com' + }; + expect(() => validateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_ID); + }); + + // Test for URL with query parameters + test('should accept URL with query parameters', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'https://example.com/api?key=value' + }; + expect(() => validateConnectionConfig(config)).not.toThrow(); + }); + + // Test for URL with path parameters + test('should accept URL with path parameters', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'https://example.com/api/{param}/value' + }; + expect(() => validateConnectionConfig(config)).not.toThrow(); + }); +}); + +describe('validateUpdateConnectionConfig', () => { + // Test for null/undefined config + test('should throw error when connectionConfig is null/undefined', () => { + expect(() => validateUpdateConnectionConfig(null)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_CONFIG); + expect(() => validateUpdateConnectionConfig(undefined)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_CONFIG); + }); + + // Test for missing connectionId + test('should throw error when connectionId is missing', () => { + const config = { + connectionUrl: 'https://example.com' + }; + expect(() => validateUpdateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_ID); + }); + + // Test for invalid connectionId + test('should throw error when connectionId is invalid', () => { + const config = { + connectionId: '', // empty string + connectionUrl: 'https://example.com' + }; + expect(() => validateUpdateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_ID); + + const config2 = { + connectionId: 123, // not a string + connectionUrl: 'https://example.com' + }; + expect(() => validateUpdateConnectionConfig(config2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_ID); + }); + + // Test for invalid connectionUrl when provided + test('should throw error when connectionUrl is invalid', () => { + const config2 = { + connectionId: 'test-connection', + connectionUrl: 'not-a-url' // invalid URL format + }; + expect(() => validateUpdateConnectionConfig(config2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_URL); + }); + + // Test for valid partial update with only connectionId + test('should accept valid config with only connectionId', () => { + const config = { + connectionId: 'test-connection' + }; + expect(() => validateUpdateConnectionConfig(config)).not.toThrow(); + }); + + // Test for valid update with all fields + test('should accept valid connection configuration update', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'https://example.com', + credentials: { + apiKey: 'sky-key-123' // Valid API key format + } + }; + expect(() => validateUpdateConnectionConfig(config)).not.toThrow(); + }); + + // Test credentials validation + test('should validate credentials if provided', () => { + const config = { + connectionId: 'test-connection', + credentials: { + apiKey: 'invalid-key' // Invalid API key format + } + }; + expect(() => validateUpdateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_API_KEY_WITH_ID); + }); + + // Test different log levels + test('should work with different log levels', () => { + const config = { + connectionId: 'test-connection' + }; + expect(() => validateUpdateConnectionConfig(config, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateUpdateConnectionConfig(config, LogLevel.INFO)).not.toThrow(); + expect(() => validateUpdateConnectionConfig(config, LogLevel.WARN)).not.toThrow(); + expect(() => validateUpdateConnectionConfig(config, LogLevel.ERROR)).not.toThrow(); + }); + + // Test for whitespace values + test('should throw error for whitespace string values', () => { + const config = { + connectionId: ' ', // whitespace only + }; + expect(() => validateUpdateConnectionConfig(config)).toThrow(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_ID); + }); + + // Test for URL with query parameters + test('should accept URL with query parameters when provided', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'https://example.com/api?key=value' + }; + expect(() => validateUpdateConnectionConfig(config)).not.toThrow(); + }); + + // Test for URL with path parameters + test('should accept URL with path parameters when provided', () => { + const config = { + connectionId: 'test-connection', + connectionUrl: 'https://example.com/api/{param}/value' + }; + expect(() => validateUpdateConnectionConfig(config)).not.toThrow(); + }); + + // Test that connectionUrl is optional + test('should not require connectionUrl', () => { + const config = { + connectionId: 'test-connection', + credentials: { + apiKey: 'sky-key-123' + } + }; + expect(() => validateUpdateConnectionConfig(config)).not.toThrow(); + }); +}); + +describe('validateInsertRequest', () => { + // Test for null/undefined request + test('should throw error when insertRequest is null/undefined', () => { + expect(() => validateInsertRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_INSERT_REQUEST); + expect(() => validateInsertRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_INSERT_REQUEST); + }); + + // Test for missing table + test('should throw error when table is missing', () => { + const request = { + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); + }); + + // Test for invalid table name + test('should throw error when table name is invalid', () => { + const request = { + table: '', // empty string + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request2 = { + table: ' ', // whitespace only + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request3 = { + table: 123, // not a string + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + }); + + // Test for missing data + test('should throw error when data is missing', () => { + const request = { + table: 'users' + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_DATA_IN_INSERT); + }); + + // Test for invalid data type + test('should throw error when data is not an array', () => { + const request = { + table: 'users', + data: { field: 'value' } // object instead of array + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TYPE_OF_DATA_IN_INSERT); + }); + + // Test for empty data array + test('should throw error when data array is empty', () => { + const request = { + table: 'users', + data: [] + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_DATA_IN_INSERT); + }); + + // Test for null/undefined records in data + test('should throw error when data contains null/undefined records', () => { + const request = { + table: 'users', + data: [null] + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_RECORD_IN_INSERT); + + const request2 = { + table: 'users', + data: [undefined] + }; + expect(() => validateInsertRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_RECORD_IN_INSERT); + }); + + // Test for invalid record types + test('should throw error when records are not objects', () => { + const request = { + table: 'users', + data: ['invalid'] // string instead of object + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_INSERT); + }); + + // Test for empty records + test('should throw error when records are empty objects', () => { + const request = { + table: 'users', + data: [{}] + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_INSERT); + }); + + // Test for invalid field names in records + test('should throw error when record fields have invalid types', () => { + const request = { + table: 'users', + data: [{ + [Symbol('key')]: 'value' // Symbol instead of string + }] + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_INSERT); + }); + + // Test insert options validation + describe('Insert Options Validation', () => { + const validRequest = { + table: 'users', + data: [{ field: 'value' }] + }; + + test('should validate returnTokens option', () => { + const options = { + getReturnTokens: () => 'not-a-boolean' + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RETURN_TOKEN); + }); + + test('should validate upsertColumn option', () => { + const options = { + getUpsertColumn: () => 123 // number instead of string + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_UPSERT); + }); + + test('should validate continueOnError option', () => { + const options = { + getContinueOnError: () => 'not-a-boolean' + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_CONTINUE_ON_ERROR); + }); + + test('should validate tokens option', () => { + const options = { + getTokens: () => 'not-an-array' + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_INSERT_TOKENS); + }); + + test('should validate token elements', () => { + const options = { + getTokens: () => [null] // null token + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.EMPTY_INSERT_TOKEN); + }); + }); + + // Test valid cases + // Test valid cases +test('should accept valid insert request', () => { + const request = { + _table: 'users', // Changed from table to _table + table: 'users', // Keep both for compatibility + data: [ + { field1: 'value1' }, + { field2: 'value2' } + ] + }; + expect(() => validateInsertRequest(request)).not.toThrow(); +}); + +// Also update other test cases that check table property +test('should throw error when table is missing', () => { + const request = { + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); +}); + +test('should throw error when table name is invalid', () => { + const request = { + _table: '', // Changed from table to _table + table: '', + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request2 = { + _table: ' ', // Changed from table to _table + table: ' ', + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request3 = { + _table: 123, // Changed from table to _table + table: 123, + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); +}); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + _table: 'users', + table: 'users', + data: [{ field: 'value' }] + }; + expect(() => validateInsertRequest(request, undefined, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateInsertRequest(request, undefined, LogLevel.INFO)).not.toThrow(); + expect(() => validateInsertRequest(request, undefined, LogLevel.WARN)).not.toThrow(); + expect(() => validateInsertRequest(request, undefined, LogLevel.ERROR)).not.toThrow(); + }); +}); + + +describe('validateUpdateRequest', () => { + // Test for null/undefined request + test('should throw error when updateRequest is null/undefined', () => { + expect(() => validateUpdateRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_UPDATE_REQUEST); + expect(() => validateUpdateRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_UPDATE_REQUEST); + }); + + // Test for missing table + test('should throw error when table is missing', () => { + const request = { + data: { skyflow_id: 'valid-id', field: 'value' } + }; + expect(() => validateUpdateRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); + }); + + // Test for invalid table name + test('should throw error when table name is invalid', () => { + const request = { + _table: '', // empty string + table: '', + data: { skyflow_id: 'valid-id', field: 'value' } + }; + expect(() => validateUpdateRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request2 = { + _table: ' ', // whitespace only + table: ' ', + data: { skyflow_id: 'valid-id', field: 'value' } + }; + expect(() => validateUpdateRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request3 = { + _table: 123, // not a string + table: 123, + data: { skyflow_id: 'valid-id', field: 'value' } + }; + expect(() => validateUpdateRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + }); + + // Test for missing data + test('should throw error when data is missing', () => { + const request = { + _table: 'users', + table: 'users' + }; + expect(() => validateUpdateRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_UPDATE_DATA); + }); + + // Test for invalid data type + test('should throw error when data is not an object', () => { + const request = { + _table: 'users', + table: 'users', + data: 'invalid' // string instead of object + }; + expect(() => validateUpdateRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TYPE_OF_UPDATE_DATA); + }); + + // Test for missing skyflow_id + test('should throw error when skyflow_id is missing', () => { + const request = { + _table: 'users', + table: 'users', + data: { field: 'value' } + }; + expect(() => validateUpdateRequest(request)).toThrow(SKYFLOW_ERROR_CODE.MISSING_SKYFLOW_ID_IN_UPDATE); + }); + + // Test for invalid skyflow_id + test('should throw error when skyflow_id is invalid', () => { + const request = { + _table: 'users', + table: 'users', + data: { skyflow_id: '' } // empty string + }; + expect(() => validateUpdateRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_SKYFLOW_ID_IN_UPDATE); + + const request2 = { + _table: 'users', + table: 'users', + data: { skyflow_id: 123 } // not a string + }; + expect(() => validateUpdateRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_SKYFLOW_ID_IN_UPDATE); + }); + + // Test update options validation + describe('Update Options Validation', () => { + const validRequest = { + _table: 'users', + table: 'users', + data: { skyflow_id: 'valid-id', field: 'value' } + }; + + test('should validate returnTokens option', () => { + const options = { + getReturnTokens: () => 'not-a-boolean' + }; + expect(() => validateUpdateRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RETURN_TOKEN); + }); + + test('should validate tokenMode option', () => { + const options = { + getTokenMode: () => 'invalid-mode' + }; + expect(() => validateUpdateRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_MODE); + }); + + test('should validate tokens option', () => { + const options = { + getTokens: () => 'not-an-object' + }; + expect(() => validateUpdateRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_UPDATE_TOKENS); + }); + }); + + // Test empty record validation + test('should throw error for empty update record', () => { + const request = { + _table: 'users', + table: 'users', + data: { + skyflow_id: 'valid-id' + // No other fields to update + } + }; + expect(() => validateUpdateRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_UPDATE); + }); + + // Test for invalid field names + test('should throw error when record fields have invalid types', () => { + const request = { + _table: 'users', + table: 'users', + data: { + skyflow_id: 'valid-id', + [Symbol('key')]: 'value' // Symbol instead of string + } + }; + expect(() => validateUpdateRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_UPDATE); + }); +}); + +describe('validateUpdateRequest - validateUpdateInput', () => { + const validTable = { + _table: 'users', + table: 'users' + }; + + // Test validateUpdateInput with empty object + test('should throw error when data object is empty', () => { + const request = { + ...validTable, + data: { + skyflow_id: 'valid-id', + // No other fields provided + } + }; + expect(() => validateUpdateRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_UPDATE); + }); + + // Test validateUpdateInput with non-string keys + test('should throw error when data contains non-string keys', () => { + const symbolKey = Symbol('test'); + const request = { + ...validTable, + data: { + skyflow_id: 'valid-id', + [symbolKey]: 'value' // Symbol key instead of string + } + }; + expect(() => validateUpdateRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_UPDATE); + }); + + // Test validateUpdateInput with invalid data type + test('should throw error when data is not an object', () => { + const request = { + ...validTable, + data: 'not-an-object' // String instead of object + }; + expect(() => validateUpdateRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TYPE_OF_UPDATE_DATA); + }); + + // Test validateUpdateInput with array data + test('should throw error when data is an array', () => { + const request = { + ...validTable, + data: ['not', 'an', 'object'] + }; + expect(() => validateUpdateRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TYPE_OF_UPDATE_DATA); + }); + + // Test validateUpdateInput with null values + test('should throw error when data contains null values', () => { + const request = { + ...validTable, + data: { + skyflow_id: 'valid-id', + field: null + } + }; + expect(() => validateUpdateRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_UPDATE); + }); + + // Test valid update data with multiple fields + test('should accept valid update data with multiple string fields', () => { + const request = { + ...validTable, + data: { + skyflowId: 'valid-id', + field1: 'value1', + field2: 'value2', + field3: 'value3' + } + }; + expect(() => validateUpdateRequest(request)).not.toThrow(); + }); + + // Test update data with nested objects + test('should accept update data with nested objects', () => { + const request = { + ...validTable, + data: { + skyflowId: 'valid-id', + field1: { + nested: 'value' + } + } + }; + expect(() => validateUpdateRequest(request)).not.toThrow(); + }); + + // Test update data with mixed value types + test('should accept update data with mixed value types', () => { + const request = { + ...validTable, + data: { + skyflowId: 'valid-id', + stringField: 'string', + objectField: { key: 'value' }, + numberField: 123, + booleanField: true + } + }; + expect(() => validateUpdateRequest(request)).not.toThrow(); + }); + + // Test error handling in validateUpdateInput + test('should handle errors in validateUpdateInput gracefully', () => { + const request = { + ...validTable, + data: Object.create(null) // Object with no prototype + }; + expect(() => validateUpdateRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_UPDATE); + }); + + // Test update with minimal valid data + test('should accept minimal valid update data', () => { + const request = { + ...validTable, + data: { + skyflowId: 'valid-id', + field1: 'value1' + } + }; + expect(() => validateUpdateRequest(request)).not.toThrow(); + }); +}); + +describe('validateGetRequest', () => { + // Test for null/undefined request + test('should throw error when getRequest is null/undefined', () => { + expect(() => validateGetRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_GET_REQUEST); + expect(() => validateGetRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_GET_REQUEST); + }); + + // Test for missing table + test('should throw error when table is missing', () => { + const request = { + ids: ['id1', 'id2'] + }; + expect(() => validateGetRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); + }); + + // Test for invalid table name + test('should throw error when table name is invalid', () => { + const request = { + _table: '', // empty string + table: '', + ids: ['id1'] + }; + expect(() => validateGetRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request2 = { + _table: ' ', // whitespace only + table: ' ', + ids: ['id1'] + }; + expect(() => validateGetRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request3 = { + _table: 123, // not a string + table: 123, + ids: ['id1'] + }; + expect(() => validateGetRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + }); + + // Test for missing ids + test('should throw error when ids is missing', () => { + const request = { + _table: 'users', + table: 'users' + }; + expect(() => validateGetRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_IDS_IN_GET); + }); + + // Test for invalid ids type + test('should throw error when ids is not an array', () => { + const request = { + _table: 'users', + table: 'users', + ids: 'invalid' // string instead of array + }; + expect(() => validateGetRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TYPE_OF_IDS); + }); + + // Test for empty ids array + test('should throw error when ids array is empty', () => { + const request = { + _table: 'users', + table: 'users', + ids: [] + }; + expect(() => validateGetRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_IDS_IN_GET); + }); + + // Test for invalid id in array + test('should throw error when id in array is invalid', () => { + const request = { + _table: 'users', + table: 'users', + ids: [null] // null id + }; + expect(() => validateGetRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_ID_IN_GET); + + const request2 = { + _table: 'users', + table: 'users', + ids: ['valid', 123] // number instead of string + }; + expect(() => validateGetRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ID_IN_GET); + }); + + // Test GetOptions validation + describe('GetOptions Validation', () => { + const validRequest = { + _table: 'users', + table: 'users', + ids: ['id1', 'id2'] + }; + + test('should validate returnTokens option', () => { + const options = { + getReturnTokens: () => 'not-a-boolean' + }; + expect(() => validateGetRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RETURN_TOKEN); + }); + + test('should validate redactionType option', () => { + const options = { + getRedactionType: () => 'INVALID_TYPE' + }; + expect(() => validateGetRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_REDACTION_TYPE); + }); + + test('should validate fields option', () => { + const options = { + getFields: () => 'not-an-array' + }; + expect(() => validateGetRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FIELDS); + }); + + test('should validate field elements', () => { + const options = { + getFields: () => [null] // null field + }; + expect(() => validateGetRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.EMPTY_FIELD); + + const options2 = { + getFields: () => ['', 'valid'] // empty string field + }; + expect(() => validateGetRequest(validRequest, options2)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FIELD); + }); + }); + + // Test valid cases + test('should accept valid get request', () => { + const request = { + _table: 'users', + table: 'users', + ids: ['id1', 'id2'] + }; + expect(() => validateGetRequest(request)).not.toThrow(); + }); + + // Test valid get request with options + test('should accept valid get request with options', () => { + const request = { + _table: 'users', + table: 'users', + ids: ['id1', 'id2'] + }; + const options = { + getReturnTokens: () => true, + getRedactionType: () => 'REDACTED', + getFields: () => ['field1', 'field2'] + }; + expect(() => validateGetRequest(request, options)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + _table: 'users', + table: 'users', + ids: ['id1'] + }; + expect(() => validateGetRequest(request, undefined, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateGetRequest(request, undefined, LogLevel.INFO)).not.toThrow(); + expect(() => validateGetRequest(request, undefined, LogLevel.WARN)).not.toThrow(); + expect(() => validateGetRequest(request, undefined, LogLevel.ERROR)).not.toThrow(); + }); +}); + +describe('validateGetColumnRequest', () => { + // Test for null/undefined request + test('should throw error when getRequest is null/undefined', () => { + expect(() => validateGetColumnRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_GET_COLUMN_REQUEST); + expect(() => validateGetColumnRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_GET_COLUMN_REQUEST); + }); + + // Test for missing table + test('should throw error when table is missing', () => { + const request = { + columnName: 'email', + columnValues: ['value1'] + }; + expect(() => validateGetColumnRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); + }); + + // Test for invalid table name + test('should throw error when table name is invalid', () => { + const request = { + _table: '', // empty string + table: '', + columnName: 'email', + columnValues: ['value1'] + }; + expect(() => validateGetColumnRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request2 = { + _table: ' ', // whitespace only + table: ' ', + columnName: 'email', + columnValues: ['value1'] + }; + expect(() => validateGetColumnRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request3 = { + _table: 123, // not a string + table: 123, + columnName: 'email', + columnValues: ['value1'] + }; + expect(() => validateGetColumnRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + }); + + // Test for missing columnName + test('should throw error when columnName is missing', () => { + const request = { + _table: 'users', + table: 'users', + columnValues: ['value1'] + }; + expect(() => validateGetColumnRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_COLUMN_NAME); + }); + + // Test for invalid columnName + test('should throw error when columnName is invalid', () => { + const request = { + _table: 'users', + table: 'users', + _columnName: '', // empty string + columnName: '', + columnValues: ['value1'] + }; + expect(() => validateGetColumnRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_NAME); + + const request2 = { + _table: 'users', + table: 'users', + _columnName: ' ', // whitespace only + columnName: ' ', + columnValues: ['value1'] + }; + expect(() => validateGetColumnRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_NAME); + + const request3 = { + _table: 'users', + table: 'users', + _columnName: 123, // not a string + columnName: 123, + columnValues: ['value1'] + }; + expect(() => validateGetColumnRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_NAME); + }); + + // Test for missing columnValues + test('should throw error when columnValues is missing', () => { + const request = { + _table: 'users', + table: 'users', + _columnName: 'email', + columnName: 'email' + }; + expect(() => validateGetColumnRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_COLUMN_VALUES); + }); + + // Test for invalid columnValues type + test('should throw error when columnValues is not an array', () => { + const request = { + _table: 'users', + table: 'users', + _columnName: 'email', + columnName: 'email', + columnValues: 'invalid' // string instead of array + }; + expect(() => validateGetColumnRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_VALUES); + }); + + // Test for empty columnValues array + test('should throw error when columnValues array is empty', () => { + const request = { + _table: 'users', + table: 'users', + _columnName: 'email', + columnName: 'email', + columnValues: [] + }; + expect(() => validateGetColumnRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_COLUMN_VALUES); + }); + + // Test for invalid columnValue in array + test('should throw error when columnValue in array is invalid', () => { + const request = { + _table: 'users', + table: 'users', + _columnName: 'email', + columnName: 'email', + columnValues: [null] // null value + }; + expect(() => validateGetColumnRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_COLUMN_VALUE); + + const request2 = { + _table: 'users', + table: 'users', + _columnName: 'email', + columnName: 'email', + columnValues: ['valid', 123] // number instead of string + }; + expect(() => validateGetColumnRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_VALUE); + }); + + // Test GetOptions validation + describe('GetOptions Validation', () => { + const validRequest = { + _table: 'users', + table: 'users', + _columnName: 'email', + columnName: 'email', + columnValues: ['value1', 'value2'] + }; + + test('should validate returnTokens option', () => { + const options = { + getReturnTokens: () => 'not-a-boolean' + }; + expect(() => validateGetColumnRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RETURN_TOKEN); + }); + + test('should validate redactionType option', () => { + const options = { + getRedactionType: () => 'INVALID_TYPE' + }; + expect(() => validateGetColumnRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_REDACTION_TYPE); + }); + }); +}); + + +describe('validateDetokenizeRequest', () => { + // Test for null/undefined request + test('should throw error when detokenizeRequest is null/undefined', () => { + expect(() => validateDetokenizeRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_DETOKENIZE_REQUEST); + expect(() => validateDetokenizeRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_DETOKENIZE_REQUEST); + }); + + // Test for missing data array + test('should throw error when data is missing', () => { + const request = {}; + expect(() => validateDetokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TOKENS_IN_DETOKENIZE); + }); + + // Test for invalid data type + test('should throw error when data is not an array', () => { + const request = { + data: 'not-an-array' + }; + expect(() => validateDetokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKENS_TYPE_IN_DETOKENIZE); + }); + + // Test for empty data array + test('should throw error when data array is empty', () => { + const request = { + data: [] + }; + expect(() => validateDetokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TOKENS_IN_DETOKENIZE); + }); + + // Test for null/undefined records in data + test('should throw error when data contains null/undefined records', () => { + const request = { + data: [null] + }; + expect(() => validateDetokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TOKEN_IN_DETOKENIZE); + + const request2 = { + data: [undefined] + }; + expect(() => validateDetokenizeRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TOKEN_IN_DETOKENIZE); + }); + + // Test for invalid token in records + test('should throw error when token is invalid', () => { + const request = { + data: [{ + token: '' // empty string + }] + }; + expect(() => validateDetokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_IN_DETOKENIZE); + + const request2 = { + data: [{ + token: ' ' // whitespace only + }] + }; + expect(() => validateDetokenizeRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_IN_DETOKENIZE); + + const request3 = { + data: [{ + token: 123 // not a string + }] + }; + expect(() => validateDetokenizeRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_IN_DETOKENIZE); + }); + + // Test for invalid redactionType + test('should throw error when redactionType is invalid', () => { + const request = { + data: [{ + token: 'valid-token', + redactionType: 'INVALID_TYPE' + }] + }; + expect(() => validateDetokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_REDACTION_TYPE); + }); + + // Test DetokenizeOptions validation + describe('DetokenizeOptions Validation', () => { + const validRequest = { + data: [{ + token: 'valid-token' + }] + }; + + test('should validate continueOnError option', () => { + const options = { + getContinueOnError: () => 'not-a-boolean' + }; + expect(() => validateDetokenizeRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_CONTINUE_ON_ERROR); + }); + + test('should validate downloadURL option', () => { + const options = { + getDownloadURL: () => 'not-a-boolean' + }; + expect(() => validateDetokenizeRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DOWNLOAD_URL); + }); + }); + + // Test valid cases + test('should accept valid detokenize request', () => { + const request = { + data: [{ + token: 'valid-token' + }] + }; + expect(() => validateDetokenizeRequest(request)).not.toThrow(); + }); + + // Test valid request with multiple tokens + test('should accept valid request with multiple tokens', () => { + const request = { + data: [ + { token: 'token1' }, + { token: 'token2' }, + { token: 'token3' } + ] + }; + expect(() => validateDetokenizeRequest(request)).not.toThrow(); + }); + + // Test valid request with redactionType + test('should accept valid request with redactionType', () => { + const request = { + data: [{ + token: 'valid-token', + redactionType: 'REDACTED' + }] + }; + expect(() => validateDetokenizeRequest(request)).not.toThrow(); + }); + + // Test valid request with options + test('should accept valid request with options', () => { + const request = { + data: [{ + token: 'valid-token' + }] + }; + const options = { + getContinueOnError: () => true, + getDownloadURL: () => false + }; + expect(() => validateDetokenizeRequest(request, options)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + data: [{ + token: 'valid-token' + }] + }; + expect(() => validateDetokenizeRequest(request, undefined, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateDetokenizeRequest(request, undefined, LogLevel.INFO)).not.toThrow(); + expect(() => validateDetokenizeRequest(request, undefined, LogLevel.WARN)).not.toThrow(); + expect(() => validateDetokenizeRequest(request, undefined, LogLevel.ERROR)).not.toThrow(); + }); +}); + +describe('validateTokenizeRequest', () => { + // Test for null/undefined request + test('should throw error when tokenizeRequest is null/undefined', () => { + expect(() => validateTokenizeRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKENIZE_REQUEST); + expect(() => validateTokenizeRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKENIZE_REQUEST); + }); + + // Test for missing values array + test('should throw error when values is missing', () => { + const request = {}; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.MISSING_VALUES_IN_TOKENIZE); + }); + + // Test for invalid values type + test('should throw error when values is not an array', () => { + const request = { + _values: 'not-an-array', + values: 'not-an-array' + }; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VALUES_TYPE_IN_TOKENIZE); + }); + + // Test for empty values array + test('should throw error when values array is empty', () => { + const request = { + _values: [], + values: [] + }; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_VALUES_IN_TOKENIZE); + }); + + // Test for null/undefined data in values + test('should throw error when values contains null/undefined data', () => { + const request = { + _values: [null], + values: [null] + }; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_DATA_IN_TOKENIZE); + + const request2 = { + _values: [undefined], + values: [undefined] + }; + expect(() => validateTokenizeRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_DATA_IN_TOKENIZE); + }); + + // Test for invalid data type in values + test('should throw error when data is not an object', () => { + const request = { + _values: ['invalid'], + values: ['invalid'] + }; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_DATA_IN_TOKENIZE); + }); + + // Test for missing value in data + test('should throw error when value is missing in data', () => { + const request = { + _values: [{ columnGroup: 'test-group' }], + values: [{ columnGroup: 'test-group' }] + }; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_VALUE_IN_TOKENIZE); + }); + + // Test for invalid value in data + test('should throw error when value is invalid', () => { + const request = { + _values: [{ + value: '', // empty string + columnGroup: 'test-group' + }], + values: [{ + value: '', + columnGroup: 'test-group' + }] + }; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VALUE_IN_TOKENIZE); + + const request2 = { + _values: [{ + value: ' ', // whitespace only + columnGroup: 'test-group' + }], + values: [{ + value: ' ', + columnGroup: 'test-group' + }] + }; + expect(() => validateTokenizeRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_VALUE_IN_TOKENIZE); + }); + + // Test for missing columnGroup + test('should throw error when columnGroup is missing', () => { + const request = { + _values: [{ value: 'test-value' }], + values: [{ value: 'test-value' }] + }; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_COLUMN_GROUP_IN_TOKENIZE); + }); + + // Test for invalid columnGroup + test('should throw error when columnGroup is invalid', () => { + const request = { + _values: [{ + value: 'test-value', + columnGroup: '' // empty string + }], + values: [{ + value: 'test-value', + columnGroup: '' + }] + }; + expect(() => validateTokenizeRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_GROUP_IN_TOKENIZE); + + const request2 = { + _values: [{ + value: 'test-value', + columnGroup: ' ' // whitespace only + }], + values: [{ + value: 'test-value', + columnGroup: ' ' + }] + }; + expect(() => validateTokenizeRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_GROUP_IN_TOKENIZE); + }); + + // Test valid cases + test('should accept valid tokenize request', () => { + const request = { + _values: [{ + value: 'test-value', + columnGroup: 'test-group' + }], + values: [{ + value: 'test-value', + columnGroup: 'test-group' + }] + }; + expect(() => validateTokenizeRequest(request)).not.toThrow(); + }); + + // Test valid request with multiple values + test('should accept valid request with multiple values', () => { + const request = { + _values: [ + { value: 'value1', columnGroup: 'group1' }, + { value: 'value2', columnGroup: 'group2' }, + { value: 'value3', columnGroup: 'group3' } + ], + values: [ + { value: 'value1', columnGroup: 'group1' }, + { value: 'value2', columnGroup: 'group2' }, + { value: 'value3', columnGroup: 'group3' } + ] + }; + expect(() => validateTokenizeRequest(request)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + _values: [{ + value: 'test-value', + columnGroup: 'test-group' + }], + values: [{ + value: 'test-value', + columnGroup: 'test-group' + }] + }; + expect(() => validateTokenizeRequest(request, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateTokenizeRequest(request, LogLevel.INFO)).not.toThrow(); + expect(() => validateTokenizeRequest(request, LogLevel.WARN)).not.toThrow(); + expect(() => validateTokenizeRequest(request, LogLevel.ERROR)).not.toThrow(); + }); +}); + +describe('validateDeleteRequest', () => { + // Test for null/undefined request + test('should throw error when deleteRequest is null/undefined', () => { + expect(() => validateDeleteRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_DELETE_REQUEST); + expect(() => validateDeleteRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_DELETE_REQUEST); + }); + + // Test for missing table + test('should throw error when table is missing', () => { + const request = { + ids: ['id1', 'id2'] + }; + expect(() => validateDeleteRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); + }); + + // Test for invalid table name + test('should throw error when table name is invalid', () => { + const request = { + _table: '', // empty string + table: '', + ids: ['id1'] + }; + expect(() => validateDeleteRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request2 = { + _table: ' ', // whitespace only + table: ' ', + ids: ['id1'] + }; + expect(() => validateDeleteRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + + const request3 = { + _table: 123, // not a string + table: 123, + ids: ['id1'] + }; + expect(() => validateDeleteRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_NAME); + }); + + // Test for missing ids + test('should throw error when ids is missing', () => { + const request = { + _table: 'users', + table: 'users' + }; + expect(() => validateDeleteRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_DELETE_IDS); + }); + + // Test for invalid ids type + test('should throw error when ids is not an array', () => { + const request = { + _table: 'users', + table: 'users', + ids: 'not-an-array' + }; + expect(() => validateDeleteRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_DELETE_IDS_INPUT); + }); + + // Test for empty ids array + test('should throw error when ids array is empty', () => { + const request = { + _table: 'users', + table: 'users', + ids: [] + }; + expect(() => validateDeleteRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_DELETE_IDS); + }); + + // Test for invalid id in array + test('should throw error when id in array is invalid', () => { + const request = { + _table: 'users', + table: 'users', + ids: [null] // null id + }; + expect(() => validateDeleteRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_ID_IN_DELETE); + + const request2 = { + _table: 'users', + table: 'users', + ids: [123] // number instead of string + }; + expect(() => validateDeleteRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ID_IN_DELETE); + + const request3 = { + _table: 'users', + table: 'users', + ids: [''] // empty string + }; + expect(() => validateDeleteRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ID_IN_DELETE); + + const request4 = { + _table: 'users', + table: 'users', + ids: [' '] // whitespace only + }; + expect(() => validateDeleteRequest(request4)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ID_IN_DELETE); + }); + + // Test valid cases + test('should accept valid delete request', () => { + const request = { + _table: 'users', + table: 'users', + ids: ['id1', 'id2'] + }; + expect(() => validateDeleteRequest(request)).not.toThrow(); + }); + + // Test with single id + test('should accept valid delete request with single id', () => { + const request = { + _table: 'users', + table: 'users', + ids: ['valid-id'] + }; + expect(() => validateDeleteRequest(request)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + _table: 'users', + table: 'users', + ids: ['valid-id'] + }; + expect(() => validateDeleteRequest(request, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateDeleteRequest(request, LogLevel.INFO)).not.toThrow(); + expect(() => validateDeleteRequest(request, LogLevel.WARN)).not.toThrow(); + expect(() => validateDeleteRequest(request, LogLevel.ERROR)).not.toThrow(); + }); + + // Test with mixed valid and invalid ids + test('should throw error when any id in array is invalid', () => { + const request = { + _table: 'users', + table: 'users', + ids: ['valid-id', ''] // second id is invalid + }; + expect(() => validateDeleteRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_ID_IN_DELETE); + }); + + // Test with undefined id in array + test('should throw error when array contains undefined', () => { + const request = { + _table: 'users', + table: 'users', + ids: ['valid-id', undefined] + }; + expect(() => validateDeleteRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_ID_IN_DELETE); + }); +}); + +describe('validateUploadFileRequest', () => { + // Test for null/undefined request + test('should throw error when uploadFileRequest is null/undefined', () => { + expect(() => validateUploadFileRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST); + expect(() => validateUploadFileRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST); + }); + + // Test for missing table + test('should throw error when table is missing', () => { + const request = { + skyflowId: 'id1', + columnName: 'file_column' + }; + expect(() => validateUploadFileRequest(request)).toThrow(SKYFLOW_ERROR_CODE.MISSING_TABLE_IN_UPLOAD_FILE); + }); + + // Test for invalid table name + test('should throw error when table name is invalid', () => { + const request = { + _table: '', // empty string + table: '', + skyflowId: 'id1', + columnName: 'file_column' + }; + expect(() => validateUploadFileRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_IN_UPLOAD_FILE); + + const request2 = { + _table: ' ', // whitespace only + table: ' ', + skyflowId: 'id1', + columnName: 'file_column' + }; + expect(() => validateUploadFileRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TABLE_IN_UPLOAD_FILE); + }); + + // Test for missing skyflowId + test('should throw error when skyflowId is missing', () => { + const request = { + _table: 'users', + table: 'users', + columnName: 'file_column' + }; + expect(() => validateUploadFileRequest(request)).toThrow(SKYFLOW_ERROR_CODE.MISSING_SKYFLOW_ID_IN_UPLOAD_FILE); + }); + + // Test for invalid skyflowId + test('should throw error when skyflowId is invalid', () => { + const request = { + _table: 'users', + table: 'users', + _skyflowId: '', // empty string + skyflowId: '', + columnName: 'file_column' + }; + expect(() => validateUploadFileRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_SKYFLOW_ID_IN_UPLOAD_FILE); + + const request2 = { + _table: 'users', + table: 'users', + _skyflowId: ' ', // whitespace only + skyflowId: ' ', + columnName: 'file_column' + }; + expect(() => validateUploadFileRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_SKYFLOW_ID_IN_UPLOAD_FILE); + }); + + // Test for missing columnName + test('should throw error when columnName is missing', () => { + const request = { + _table: 'users', + table: 'users', + _skyflowId: 'id1', + skyflowId: 'id1' + }; + expect(() => validateUploadFileRequest(request)).toThrow(SKYFLOW_ERROR_CODE.MISSING_COLUMN_NAME_IN_UPLOAD_FILE); + }); + + // Test for invalid columnName + test('should throw error when columnName is invalid', () => { + const request = { + _table: 'users', + table: 'users', + _skyflowId: 'id1', + skyflowId: 'id1', + _columnName: '', // empty string + columnName: '' + }; + expect(() => validateUploadFileRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_NAME_IN_UPLOAD_FILE); + }); + + // Test FileUploadOptions validation + describe('FileUploadOptions Validation', () => { + const validRequest = { + _table: 'users', + table: 'users', + _skyflowId: 'id1', + skyflowId: 'id1', + _columnName: 'file_column', + columnName: 'file_column' + }; + + // Test invalid filePath + test('should throw error when filePath is invalid', () => { + const options = { + getFilePath: () => 123 // number instead of string + }; + expect(() => validateUploadFileRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_PATH_IN_UPLOAD_FILE); + }); + + // Test invalid base64 + test('should throw error when base64 is invalid', () => { + const options = { + getBase64: () => 123 // number instead of string + }; + expect(() => validateUploadFileRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_BASE64_IN_UPLOAD_FILE); + }); + + // Test invalid fileObject + test('should throw error when fileObject is invalid', () => { + const options = { + getFileObject: () => "not-a-file-object" // string instead of File object + }; + expect(() => validateUploadFileRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_OBJECT_IN_UPLOAD_FILE); + }); + + // Test missing file source + test('should throw error when no file source is provided', () => { + const options = { + getFilePath: () => null, + getBase64: () => null, + getFileObject: () => null + }; + expect(() => validateUploadFileRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.MISSING_FILE_SOURCE_IN_UPLOAD_FILE); + }); + + // Test missing fileName for base64 + test('should throw error when fileName is missing for base64', () => { + const options = { + getBase64: () => 'valid-base64', + getFileName: () => null + }; + expect(() => validateUploadFileRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.MISSING_FILE_NAME_FOR_BASE64); + }); + + // Test invalid File object + test('should throw error when File object is invalid', () => { + const options = { + getFileObject: () => ({}) // not a File instance + }; + expect(() => validateUploadFileRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_OBJECT_IN_UPLOAD_FILE); + }); + + // Test missing filename in File object + test('should throw error when filename is missing in File object', () => { + const mockFile = new File([], ''); // empty filename + const options = { + getFileObject: () => mockFile + }; + expect(() => validateUploadFileRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.MISSING_FILE_NAME_IN_FILE_OBJECT); + }); + }); + + // Test valid cases + test('should accept valid upload request with filePath', () => { + const request = { + _table: 'users', + table: 'users', + _skyflowId: 'id1', + skyflowId: 'id1', + _columnName: 'file_column', + columnName: 'file_column' + }; + const options = { + getFilePath: () => '/valid/path/to/file.txt', + getBase64: () => null, + getFileObject: () => null + }; + expect(() => validateUploadFileRequest(request, options)).not.toThrow(); + }); + + test('should accept valid upload request with base64', () => { + const request = { + _table: 'users', + table: 'users', + _skyflowId: 'id1', + skyflowId: 'id1', + _columnName: 'file_column', + columnName: 'file_column' + }; + const options = { + getFilePath: () => null, + getBase64: () => 'valid-base64', + getFileName: () => 'file.txt', + getFileObject: () => null + }; + expect(() => validateUploadFileRequest(request, options)).not.toThrow(); + }); + + test('should accept valid upload request with File object', () => { + const request = { + _table: 'users', + table: 'users', + _skyflowId: 'id1', + skyflowId: 'id1', + _columnName: 'file_column', + columnName: 'file_column' + }; + const mockFile = new File(['test'], 'test.txt'); + const options = { + getFilePath: () => null, + getBase64: () => null, + getFileObject: () => mockFile + }; + expect(() => validateUploadFileRequest(request, options)).not.toThrow(); + }); +}); + +describe('validateQueryRequest', () => { + // Test for null/undefined request + test('should throw error when queryRequest is null/undefined', () => { + expect(() => validateQueryRequest(null)).toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY_REQUEST); + expect(() => validateQueryRequest(undefined)).toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY_REQUEST); + }); + + // Test for missing query + test('should throw error when query is missing', () => { + const request = {}; + expect(() => validateQueryRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_QUERY); + }); + + // Test for missing _query property + test('should throw error when _query property is missing', () => { + const request = { + query: 'SELECT * FROM users' + }; + expect(() => validateQueryRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_QUERY); + }); + + // Test for invalid query type + test('should throw error when query is not a string', () => { + const request = { + _query: 123, + query: 123 // number instead of string + }; + expect(() => validateQueryRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY); + + const request2 = { + _query: {}, + query: {} // object instead of string + }; + expect(() => validateQueryRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY); + }); + + // Test for empty query string + test('should throw error when query is empty string', () => { + const request = { + _query: '', + query: '' + }; + expect(() => validateQueryRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY); + }); + + // Test for whitespace-only query + test('should throw error when query contains only whitespace', () => { + const request = { + _query: ' ', + query: ' ' + }; + expect(() => validateQueryRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY); + }); + + // Test valid query + test('should accept valid query request', () => { + const request = { + _query: 'SELECT * FROM users', + query: 'SELECT * FROM users' + }; + expect(() => validateQueryRequest(request)).not.toThrow(); + }); + + // Test complex valid query + test('should accept valid complex query', () => { + const request = { + _query: 'SELECT id, name FROM users WHERE age > 18 ORDER BY name DESC', + query: 'SELECT id, name FROM users WHERE age > 18 ORDER BY name DESC' + }; + expect(() => validateQueryRequest(request)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + _query: 'SELECT * FROM users', + query: 'SELECT * FROM users' + }; + expect(() => validateQueryRequest(request, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateQueryRequest(request, LogLevel.INFO)).not.toThrow(); + expect(() => validateQueryRequest(request, LogLevel.WARN)).not.toThrow(); + expect(() => validateQueryRequest(request, LogLevel.ERROR)).not.toThrow(); + }); + + // Test query with special characters + test('should accept query with special characters', () => { + const request = { + _query: 'SELECT * FROM `my-table` WHERE `column-name` = "value"', + query: 'SELECT * FROM `my-table` WHERE `column-name` = "value"' + }; + expect(() => validateQueryRequest(request)).not.toThrow(); + }); + + // Test query with multiple lines + test('should accept multi-line query', () => { + const request = { + _query: ` + SELECT * + FROM users + WHERE age > 18 + `, + query: ` + SELECT * + FROM users + WHERE age > 18 + ` + }; + expect(() => validateQueryRequest(request)).not.toThrow(); + }); +}); + +describe('validateDeIdentifyTextRequest', () => { + // Test for invalid text + test('should throw error when text is missing', () => { + const request = {}; + expect(() => validateDeIdentifyTextRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TEXT_IN_DEIDENTIFY); + }); + + test('should throw error when text is empty', () => { + const request = { + text: '' + }; + expect(() => validateDeIdentifyTextRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TEXT_IN_DEIDENTIFY); + }); + + test('should throw error when text is whitespace', () => { + const request = { + text: ' ' + }; + expect(() => validateDeIdentifyTextRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TEXT_IN_DEIDENTIFY); + }); + + test('should throw error when text is not string', () => { + const request = { + text: 123 + }; + expect(() => validateDeIdentifyTextRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TEXT_IN_DEIDENTIFY); + }); + + // Test Options Validation + describe('Options Validation', () => { + const validRequest = { + text: 'valid text' + }; + + test('should throw error when entities is not an array', () => { + const options = { + getEntities: () => 'not-an-array' + }; + expect(() => validateDeIdentifyTextRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_ENTITIES_IN_DEIDENTIFY); + }); + + test('should throw error when allowRegexList is not an array', () => { + const options = { + getAllowRegexList: () => 'not-an-array' + }; + expect(() => validateDeIdentifyTextRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_ALLOW_REGEX_LIST); + }); + + test('should throw error when restrictRegexList is not an array', () => { + const options = { + getRestrictRegexList: () => 'not-an-array' + }; + expect(() => validateDeIdentifyTextRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RESTRICT_REGEX_LIST); + }); + + test('should throw error when tokenFormat is not TokenFormat instance', () => { + const options = { + getTokenFormat: () => ({}) // not a TokenFormat instance + }; + expect(() => validateDeIdentifyTextRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_FORMAT); + }); + + test('should throw error when transformations is not Transformations instance', () => { + const options = { + getTransformations: () => ({}) // not a Transformations instance + }; + expect(() => validateDeIdentifyTextRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TRANSFORMATIONS); + }); + }); + + // Test valid cases + test('should accept valid request without options', () => { + const request = { + text: 'valid text' + }; + expect(() => validateDeIdentifyTextRequest(request)).not.toThrow(); + }); + + test('should accept valid request with valid options', () => { + const request = { + text: 'valid text' + }; + const options = { + getEntities: () => ['PERSON', 'EMAIL'], + getAllowRegexList: () => ['^test', 'test$'], + getRestrictRegexList: () => ['^restricted', 'restricted$'], + getTokenFormat: () => new TokenFormat(), + getTransformations: () => new Transformations() + }; + expect(() => validateDeIdentifyTextRequest(request, options)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + text: 'valid text' + }; + expect(() => validateDeIdentifyTextRequest(request, undefined, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateDeIdentifyTextRequest(request, undefined, LogLevel.INFO)).not.toThrow(); + expect(() => validateDeIdentifyTextRequest(request, undefined, LogLevel.WARN)).not.toThrow(); + expect(() => validateDeIdentifyTextRequest(request, undefined, LogLevel.ERROR)).not.toThrow(); + }); + + // Test with various text content + test('should accept text with special characters', () => { + const request = { + text: 'Special chars: !@#$%^&*()' + }; + expect(() => validateDeIdentifyTextRequest(request)).not.toThrow(); + }); + + test('should accept multi-line text', () => { + const request = { + text: `Line 1 + Line 2 + Line 3` + }; + expect(() => validateDeIdentifyTextRequest(request)).not.toThrow(); + }); + + test('should accept text with multiple spaces', () => { + const request = { + text: 'Text with multiple spaces' + }; + expect(() => validateDeIdentifyTextRequest(request)).not.toThrow(); + }); +}); + +describe('validateReidentifyTextRequest', () => { + // Test for invalid text + test('should throw error when text is missing', () => { + const request = {}; + expect(() => validateReidentifyTextRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TEXT_IN_REIDENTIFY); + }); + + test('should throw error when text is empty', () => { + const request = { + text: '' + }; + expect(() => validateReidentifyTextRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TEXT_IN_REIDENTIFY); + }); + + test('should throw error when text is whitespace', () => { + const request = { + text: ' ' + }; + expect(() => validateReidentifyTextRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TEXT_IN_REIDENTIFY); + }); + + test('should throw error when text is not string', () => { + const request = { + text: 123 + }; + expect(() => validateReidentifyTextRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_TEXT_IN_REIDENTIFY); + }); + + // Test Options Validation + describe('ReidentifyTextOptions Validation', () => { + const validRequest = { + text: 'valid text with tokens' + }; + + test('should throw error when redactedEntities is not an array', () => { + const options = { + getRedactedEntities: () => 'not-an-array' + }; + expect(() => validateReidentifyTextRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_REDACTED_ENTITIES_IN_REIDENTIFY); + }); + + test('should throw error when maskedEntities is not an array', () => { + const options = { + getMaskedEntities: () => 'not-an-array' + }; + expect(() => validateReidentifyTextRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_MASKED_ENTITIES_IN_REIDENTIFY); + }); + + test('should throw error when plainTextEntities is not an array', () => { + const options = { + getPlainTextEntities: () => 'not-an-array' + }; + expect(() => validateReidentifyTextRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_PLAIN_TEXT_ENTITIES_IN_REIDENTIFY); + }); + + test('should accept valid arrays for all entity types', () => { + const options = { + getRedactedEntities: () => ['PERSON', 'EMAIL'], + getMaskedEntities: () => ['PHONE', 'SSN'], + getPlainTextEntities: () => ['ADDRESS'] + }; + expect(() => validateReidentifyTextRequest(validRequest, options)).not.toThrow(); + }); + + test('should accept empty arrays for entity types', () => { + const options = { + getRedactedEntities: () => [], + getMaskedEntities: () => [], + getPlainTextEntities: () => [] + }; + expect(() => validateReidentifyTextRequest(validRequest, options)).not.toThrow(); + }); + }); + + // Test valid cases + test('should accept valid request without options', () => { + const request = { + text: 'valid text with tokens' + }; + expect(() => validateReidentifyTextRequest(request)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + text: 'valid text with tokens' + }; + expect(() => validateReidentifyTextRequest(request, undefined, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateReidentifyTextRequest(request, undefined, LogLevel.INFO)).not.toThrow(); + expect(() => validateReidentifyTextRequest(request, undefined, LogLevel.WARN)).not.toThrow(); + expect(() => validateReidentifyTextRequest(request, undefined, LogLevel.ERROR)).not.toThrow(); + }); + + // Test with various text content + test('should accept text with special characters', () => { + const request = { + text: 'Text with special chars: !@#$%^&*()' + }; + expect(() => validateReidentifyTextRequest(request)).not.toThrow(); + }); + + test('should accept multi-line text', () => { + const request = { + text: `Line 1 + Line 2 + Line 3` + }; + expect(() => validateReidentifyTextRequest(request)).not.toThrow(); + }); + + test('should accept text with tokens', () => { + const request = { + text: 'Text with {{TOKEN_1}} and {{TOKEN_2}} embedded' + }; + expect(() => validateReidentifyTextRequest(request)).not.toThrow(); + }); + + // Test combinations of options + test('should accept request with mixed entity types', () => { + const request = { + text: 'Text with tokens' + }; + const options = { + getRedactedEntities: () => ['PERSON'], + getMaskedEntities: () => ['EMAIL'], + getPlainTextEntities: () => ['PHONE'] + }; + expect(() => validateReidentifyTextRequest(request, options)).not.toThrow(); + }); +}); + +describe('validateGetDetectRunRequest', () => { + // Test for missing runId + test('should throw error when runId is missing', () => { + const request = {}; + expect(() => validateGetDetectRunRequest(request)).toThrow(SKYFLOW_ERROR_CODE.EMPTY_RUN_ID); + }); + + // Test for invalid runId type + test('should throw error when runId is not a string', () => { + const request = { + runId: 123 // number instead of string + }; + expect(() => validateGetDetectRunRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RUN_ID); + + const request2 = { + runId: {} // object instead of string + }; + expect(() => validateGetDetectRunRequest(request2)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RUN_ID); + + const request3 = { + runId: true // boolean instead of string + }; + expect(() => validateGetDetectRunRequest(request3)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RUN_ID); + }); + + // Test for empty runId + test('should throw error when runId is empty string', () => { + const request = { + runId: '' + }; + expect(() => validateGetDetectRunRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RUN_ID); + }); + + // Test for whitespace runId + test('should throw error when runId contains only whitespace', () => { + const request = { + runId: ' ' + }; + expect(() => validateGetDetectRunRequest(request)).toThrow(SKYFLOW_ERROR_CODE.INVALID_RUN_ID); + }); + + // Test valid cases + test('should accept valid request', () => { + const request = { + runId: 'valid-run-id' + }; + expect(() => validateGetDetectRunRequest(request)).not.toThrow(); + }); + + // Test for different valid runId formats + test('should accept different valid runId formats', () => { + const request1 = { + runId: 'run-123' + }; + expect(() => validateGetDetectRunRequest(request1)).not.toThrow(); + + const request2 = { + runId: 'RUN_456' + }; + expect(() => validateGetDetectRunRequest(request2)).not.toThrow(); + + const request3 = { + runId: '789abc' + }; + expect(() => validateGetDetectRunRequest(request3)).not.toThrow(); + }); + + // Test different log levels + test('should work with different log levels', () => { + const request = { + runId: 'valid-run-id' + }; + expect(() => validateGetDetectRunRequest(request, LogLevel.DEBUG)).not.toThrow(); + expect(() => validateGetDetectRunRequest(request, LogLevel.INFO)).not.toThrow(); + expect(() => validateGetDetectRunRequest(request, LogLevel.WARN)).not.toThrow(); + expect(() => validateGetDetectRunRequest(request, LogLevel.ERROR)).not.toThrow(); + }); + + // Test special characters in runId + test('should accept runId with special characters', () => { + const request = { + runId: 'run-id_123.456@test' + }; + expect(() => validateGetDetectRunRequest(request)).not.toThrow(); + }); + +}); + +describe('validateInvokeConnectionRequest', () => { + // Test for null/undefined request + test('should throw error when request is null/undefined', () => { + expect(() => validateInvokeConnectionRequest(null)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_INVOKE_CONNECTION_REQUEST); + expect(() => validateInvokeConnectionRequest(undefined)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_INVOKE_CONNECTION_REQUEST); + }); + + // Test for missing method + test('should throw error when method is missing', () => { + const request = {}; + expect(() => validateInvokeConnectionRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.EMPTY_METHOD_NAME); + }); + + // Test for invalid method + test('should throw error when method is invalid', () => { + const request = { + method: 'INVALID_METHOD' + }; + expect(() => validateInvokeConnectionRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_METHOD_NAME); + }); + + // Test for invalid queryParams + test('should throw error when queryParams is invalid', () => { + const request = { + method: 'GET', + queryParams: 'not-an-object' // string instead of object + }; + expect(() => validateInvokeConnectionRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY_PARAMS); + + const request2 = { + method: 'GET', + queryParams: [1, 2, 3] // array instead of object + }; + expect(() => validateInvokeConnectionRequest(request2)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY_PARAMS); + + const request3 = { + method: 'GET', + queryParams: { + param1: 123 // number instead of string + } + }; + expect(() => validateInvokeConnectionRequest(request3)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_QUERY_PARAMS); + }); + + // Test for invalid pathParams + test('should throw error when pathParams is invalid', () => { + const request = { + method: 'GET', + pathParams: 'not-an-object' + }; + expect(() => validateInvokeConnectionRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_PATH_PARAMS); + + const request2 = { + method: 'GET', + pathParams: { + param1: true // boolean instead of string + } + }; + expect(() => validateInvokeConnectionRequest(request2)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_PATH_PARAMS); + }); + + // Test for invalid body + test('should throw error when body is invalid', () => { + const request = { + method: 'POST', + body: 'not-an-object' + }; + expect(() => validateInvokeConnectionRequest(request)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_BODY); + + const request2 = { + method: 'POST', + body: { + field: Symbol() // symbol instead of string or object + } + }; + expect(() => validateInvokeConnectionRequest(request2)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_BODY); + }); + + // Test valid cases + test('should accept valid request with minimal params', () => { + const request = { + method: 'GET' + }; + expect(() => validateInvokeConnectionRequest(request)).not.toThrow(); + }); + + test('should accept valid request with all params', () => { + const request = { + method: 'POST', + queryParams: { + param1: 'value1', + param2: 'value2' + }, + pathParams: { + id: '123' + }, + body: { + field1: 'value1', + field2: { + nested: 'value' + } + }, + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer token' + } + }; + expect(() => validateInvokeConnectionRequest(request)).not.toThrow(); + }); + + // Test nested objects in body + test('should accept nested objects in body', () => { + const request = { + method: 'POST', + body: { + level1: { + level2: { + level3: 'value' + } + } + } + }; + expect(() => validateInvokeConnectionRequest(request)).not.toThrow(); + }); + + // Test empty objects in optional params + test('should accept empty objects for optional params', () => { + const request = { + method: 'GET', + queryParams: {}, + pathParams: {}, + body: {}, + headers: {} + }; + expect(() => validateInvokeConnectionRequest(request)).not.toThrow(); + }); +}); + + +describe('validateUpdateOptions - validateUpdateToken', () => { + // Test validateUpdateToken with empty object + test('should throw error when tokens object is empty', () => { + const options = { + getTokens: () => ({}) + }; + expect(() => validateUpdateOptions(options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_IN_UPDATE); + }); + + // Test validateUpdateToken with non-string keys + test('should throw error when tokens contain non-string keys', () => { + const symbolKey = Symbol('test'); + const options = { + getTokens: () => ({ + [symbolKey]: 'value' // Symbol key instead of string + }) + }; + expect(() => validateUpdateOptions(options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_IN_UPDATE); + }); + + // Test validateUpdateToken with multiple valid tokens + test('should accept tokens with multiple string fields', () => { + const options = { + getTokens: () => ({ + field1: 'token1', + field2: 'token2', + field3: 'token3' + }) + }; + expect(() => validateUpdateOptions(options)).not.toThrow(); + }); + + // Test validateUpdateToken with nested objects + test('should accept tokens with nested objects', () => { + const options = { + getTokens: () => ({ + field1: { + nestedToken: 'value' + } + }) + }; + expect(() => validateUpdateOptions(options)).not.toThrow(); + }); + + // Test validateUpdateToken with mixed value types + test('should accept tokens with mixed value types', () => { + const options = { + getTokens: () => ({ + stringToken: 'token1', + objectToken: { key: 'value' }, + numberToken: 123, + booleanToken: true + }) + }; + expect(() => validateUpdateOptions(options)).not.toThrow(); + }); + + // Test validateUpdateToken with non-object token values + test('should throw error when tokens is not an object', () => { + const options = { + getTokens: () => 'not-an-object' + }; + expect(() => validateUpdateOptions(options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_UPDATE_TOKENS); + }); + + // Test complete update options with valid tokens + test('should accept valid update options with tokens', () => { + const options = { + getReturnTokens: () => true, + getTokenMode: () => TokenMode.ENABLE, + getTokens: () => ({ + field1: 'token1', + field2: 'token2' + }) + }; + expect(() => validateUpdateOptions(options)).not.toThrow(); + }); +}); + +describe('validateInsertRequest - validateInsertOptions', () => { + const validRequest = { + _table: 'users', + table: 'users', + data: [{ field: 'value' }] + }; + + // Test returnTokens validation + test('should throw error when returnTokens is not boolean', () => { + const options = { + getReturnTokens: () => 'not-a-boolean' + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RETURN_TOKEN); + }); + + // Test upsertColumn validation + test('should throw error when upsertColumn is not string', () => { + const options = { + getUpsertColumn: () => 123 // number instead of string + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_UPSERT); + }); + + // Test continueOnError validation + test('should throw error when continueOnError is not boolean', () => { + const options = { + getContinueOnError: () => 'not-a-boolean' + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_CONTINUE_ON_ERROR); + }); + + // Test homogeneous validation + test('should throw error when homogeneous is not boolean', () => { + const options = { + getHomogeneous: () => 'not-a-boolean' + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_HOMOGENEOUS); + }); + + // Test tokenMode validation + test('should throw error when tokenMode is invalid', () => { + const options = { + getTokenMode: () => 'INVALID_MODE' + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_MODE); + }); + + // Test tokens validation + test('should throw error when tokens is not an array', () => { + const options = { + getTokens: () => 'not-an-array' + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_INSERT_TOKENS); + }); + + // Test token elements validation + test('should throw error when token element is null', () => { + const options = { + getTokens: () => [null] + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.EMPTY_INSERT_TOKEN); + }); + + test('should throw error when token element is not an object', () => { + const options = { + getTokens: () => ['not-an-object'] + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_INSERT_TOKEN); + }); + + // Test valid combinations + test('should accept valid insert options', () => { + const options = { + getReturnTokens: () => true, + getUpsertColumn: () => 'id', + getContinueOnError: () => true, + getHomogeneous: () => true, + getTokenMode: () => TokenMode.ENABLE, + getTokens: () => [{ token: 'valid-token' }] + }; + expect(() => validateInsertRequest(validRequest, options)).not.toThrow(); + }); + + // Test optional parameters + test('should accept undefined options', () => { + expect(() => validateInsertRequest(validRequest, undefined)).not.toThrow(); + }); + + // Test partial options + test('should accept valid insert options', () => { + const options = { + getReturnTokens: () => true, + getUpsertColumn: () => 'id', + getContinueOnError: () => true, + getHomogeneous: () => true, + getTokenMode: () => 'ENABLE', + getTokens: () => [{ token: 'valid-token' }] + }; + expect(() => validateInsertRequest(validRequest, options)).not.toThrow(); +}); + + // Test token strict mode validation + test('should throw error in ENABLE_STRICT mode when token count mismatches', () => { + const request = { + _table: 'users', + table: 'users', + data: [ + { field1: 'value1' }, + { field2: 'value2' } + ] + }; + const options = { + getTokenMode: () => TokenMode.ENABLE_STRICT, + getTokens: () => [{ token: 'token1' }] // Only one token for two records + }; + expect(() => validateInsertRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INSUFFICIENT_TOKENS_PASSED_FOR_TOKEN_MODE_ENABLE_STRICT); + }); + + test('should throw error in ENABLE mode when tokens are missing', () => { + const options = { + getTokenMode: () => TokenMode.ENABLE + // No tokens provided + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.NO_TOKENS_WITH_TOKEN_MODE); + }); + + // Test complex data scenarios + test('should validate options with multiple records and tokens', () => { + const request = { + _table: 'users', + table: 'users', + data: [ + { field1: 'value1' }, + { field2: 'value2' } + ] + }; + const options = { + getTokenMode: () => TokenMode.ENABLE_STRICT, + getTokens: () => [ + { field1: 'token1' }, + { field2: 'token2' } + ], + getReturnTokens: () => true + }; + expect(() => validateInsertRequest(request, options)).not.toThrow(); + }); +}); + +describe('validateInsertRequest - validateTokensForInsertRequest', () => { + const validRequest = { + _table: 'users', + table: 'users', + data: [ + { field1: 'value1' }, + { field2: 'value2' } + ] + }; + + // Test when token mode is ENABLE but tokens are missing + test('should throw error when token mode is ENABLE but tokens are missing', () => { + const options = { + getTokenMode: () => 'ENABLE', + getTokens: () => undefined + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.NO_TOKENS_WITH_TOKEN_MODE); + }); + + // Test when token mode is ENABLE_STRICT but tokens are missing + test('should throw error when token mode is ENABLE_STRICT but tokens are missing', () => { + const options = { + getTokenMode: () => 'ENABLE_STRICT', + getTokens: () => undefined + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.NO_TOKENS_WITH_TOKEN_MODE); + }); + + // Test token count mismatch in ENABLE_STRICT mode + test('should throw error when token count does not match data count in ENABLE_STRICT mode', () => { + const options = { + getTokenMode: () => 'ENABLE_STRICT', + getTokens: () => [ + { field1: 'token1' } // Only one token for two records + ] + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INSUFFICIENT_TOKENS_PASSED_FOR_TOKEN_MODE_ENABLE_STRICT); + }); + + // Test token field mismatch in ENABLE_STRICT mode + test('should throw error when token fields do not match data fields in ENABLE_STRICT mode', () => { + const options = { + getTokenMode: () => 'ENABLE_STRICT', + getTokens: () => [ + { differentField: 'token1' }, + { anotherField: 'token2' } + ] + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INSUFFICIENT_TOKENS_PASSED_FOR_TOKEN_MODE_ENABLE_STRICT); + }); + + // Test valid case with ENABLE mode + test('should accept valid request with ENABLE mode and tokens', () => { + const options = { + getTokenMode: () => 'ENABLE', + getTokens: () => [ + { field1: 'token1' }, + { field2: 'token2' } + ] + }; + expect(() => validateInsertRequest(validRequest, options)).not.toThrow(); + }); + + // Test valid case with ENABLE_STRICT mode + test('should accept valid request with ENABLE_STRICT mode and matching tokens', () => { + const request = { + _table: 'users', + table: 'users', + data: [ + { field1: 'value1' } + ] + }; + const options = { + getTokenMode: () => 'ENABLE_STRICT', + getTokens: () => [ + { field1: 'token1' } + ] + }; + expect(() => validateInsertRequest(request, options)).not.toThrow(); + }); + + // Test with multiple fields in ENABLE_STRICT mode + test('should accept valid request with multiple fields in ENABLE_STRICT mode', () => { + const request = { + _table: 'users', + table: 'users', + data: [ + { + field1: 'value1', + field2: 'value2' + } + ] + }; + const options = { + getTokenMode: () => 'ENABLE_STRICT', + getTokens: () => [ + { + field1: 'token1', + field2: 'token2' + } + ] + }; + expect(() => validateInsertRequest(request, options)).not.toThrow(); + }); + + // Test DISABLE mode + test('should accept request with DISABLE mode without tokens', () => { + const options = { + getTokenMode: () => 'DISABLE' + }; + expect(() => validateInsertRequest(validRequest, options)).not.toThrow(); + }); + + // Test without token mode + test('should accept request without token mode', () => { + const options = { + getReturnTokens: () => true, + getUpsertColumn: () => 'id', + getContinueOnError: () => true, + getHomogeneous: () => true, + getTokenMode: () => 'DISABLE', + getTokens: () => [{ token: 'valid-token' }] + }; + expect(() => validateInsertRequest(validRequest, options)).not.toThrow(); + }); + + // Test with empty tokens array in ENABLE_STRICT mode + test('should throw error with empty tokens array in ENABLE_STRICT mode', () => { + const options = { + getTokenMode: () => 'ENABLE_STRICT', + getTokens: () => [] + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INSUFFICIENT_TOKENS_PASSED_FOR_TOKEN_MODE_ENABLE_STRICT); + }); + + // Test with null tokens in ENABLE mode + test('should throw error with null tokens in ENABLE mode', () => { + const options = { + getTokenMode: () => 'ENABLE', + getTokens: () => null + }; + expect(() => validateInsertRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.NO_TOKENS_WITH_TOKEN_MODE); + }); + + // Test validateTokensMapWithTokenStrict with missing fields + test('should throw error when token map is missing required fields', () => { + const request = { + _table: 'users', + table: 'users', + data: [ + { + field1: 'value1', + field2: 'value2', + field3: 'value3' + } + ] + }; + const options = { + getTokenMode: () => 'ENABLE_STRICT', + getTokens: () => [ + { + field1: 'token1', + field2: 'token2' + // field3 is missing + } + ] + }; + expect(() => validateInsertRequest(request, options)) + .toThrow(SKYFLOW_ERROR_CODE.INSUFFICIENT_TOKENS_PASSED_FOR_TOKEN_MODE_ENABLE_STRICT); + }); +}); + +describe('validateUpdateRequest - validateUpdateOptions', () => { + const validRequest = { + _table: 'users', + table: 'users', + data: { + skyflowId: 'valid-id', + field1: 'value1' + } + }; + + // Test returnTokens validation + test('should throw error when returnTokens is not boolean', () => { + const options = { + getReturnTokens: () => 'not-a-boolean' + }; + expect(() => validateUpdateRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RETURN_TOKEN); + }); + + // Test tokenMode validation + test('should throw error when tokenMode is invalid', () => { + const options = { + getTokenMode: () => 'INVALID_MODE' + }; + expect(() => validateUpdateRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_MODE); + }); + + // Test tokens validation + test('should throw error when tokens is not an object', () => { + const options = { + getTokens: () => 'not-an-object' + }; + expect(() => validateUpdateRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_UPDATE_TOKENS); + }); + + // Test empty tokens object + test('should throw error when tokens object is empty', () => { + const options = { + getTokens: () => ({}) + }; + expect(() => validateUpdateRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_IN_UPDATE); + }); + + // Test invalid token keys + test('should throw error when tokens contain non-string keys', () => { + const symbolKey = Symbol('test'); + const options = { + getTokens: () => ({ + [symbolKey]: 'value' // Symbol key instead of string + }) + }; + expect(() => validateUpdateRequest(validRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_TOKEN_IN_UPDATE); + }); + + // Test valid tokens with different value types + test('should accept tokens with various value types', () => { + const options = { + getTokens: () => ({ + field1: 'string-token', + field2: { nested: 'value' }, + field3: 123, + field4: true + }) + }; + expect(() => validateUpdateRequest(validRequest, options)).not.toThrow(); + }); + + // Test valid options with all properties + test('should accept valid update options with all properties', () => { + const options = { + getReturnTokens: () => true, + getTokenMode: () => 'ENABLE', + getTokens: () => ({ + field1: 'token1', + field2: 'token2' + }) + }; + expect(() => validateUpdateRequest(validRequest, options)).not.toThrow(); + }); + + // Test partial valid options + test('should accept valid options with only returnTokens', () => { + const options = { + getReturnTokens: () => true + }; + expect(() => validateUpdateRequest(validRequest, options)).not.toThrow(); + }); + + // Test undefined options + test('should accept request without options', () => { + expect(() => validateUpdateRequest(validRequest)).not.toThrow(); + }); + + // Test empty options object + test('should accept empty options object', () => { + const options = {}; + expect(() => validateUpdateRequest(validRequest, options)).not.toThrow(); + }); + + // Test with different TokenMode values + test('should accept valid token modes', () => { + const validModes = ['ENABLE', 'DISABLE']; + validModes.forEach(mode => { + const options = { + getTokenMode: () => mode + }; + expect(() => validateUpdateRequest(validRequest, options)).not.toThrow(); + }); + }); + + // Test complex nested tokens + test('should accept tokens with nested structures', () => { + const options = { + getTokens: () => ({ + field1: { + nested1: { + nested2: 'token-value' + } + }, + field2: 'simple-token' + }) + }; + expect(() => validateUpdateRequest(validRequest, options)).not.toThrow(); + }); +}); + +describe('validateGetRequest/validateGetColumnRequest - validateGetOptions', () => { + const validGetRequest = { + _table: 'users', + table: 'users', + ids: ['id1'] + }; + + const validGetColumnRequest = { + _table: 'users', + table: 'users', + _columnName: 'email', + columnName: 'email', + columnValues: ['value1'] + }; + + // Test returnTokens validation + test('should throw error when returnTokens is not boolean', () => { + const options = { + getReturnTokens: () => 'not-a-boolean' + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_RETURN_TOKEN); + }); + + // Test redactionType validation + test('should throw error when redactionType is invalid', () => { + const options = { + getRedactionType: () => 'INVALID_TYPE' + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_REDACTION_TYPE); + }); + + // Test offset validation + test('should throw error when offset is not string', () => { + const options = { + getOffset: () => 123 // number instead of string + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_OFFSET); + }); + + // Test limit validation + test('should throw error when limit is not string', () => { + const options = { + getLimit: () => 123 // number instead of string + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_LIMIT); + }); + + // Test downloadURL validation + test('should throw error when downloadURL is not boolean', () => { + const options = { + getDownloadURL: () => 'not-a-boolean' + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_DOWNLOAD_URL); + }); + + // Test columnName validation + test('should throw error when columnName is not string', () => { + const options = { + getColumnName: () => 123 // number instead of string + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_NAME); + }); + + // Test orderBy validation + test('should throw error when orderBy is invalid', () => { + const options = { + getOrderBy: () => 'INVALID_ORDER' + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_ORDER_BY); + }); + + // Test fields validation + test('should throw error when fields is not an array', () => { + const options = { + getFields: () => 'not-an-array' + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FIELDS); + }); + + // Test fields array elements + test('should throw error when fields contain invalid elements', () => { + const options = { + getFields: () => [null] + }; + expect(() => validateGetRequest(validGetRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.EMPTY_FIELD); + + const options2 = { + getFields: () => [''] // empty string + }; + expect(() => validateGetRequest(validGetRequest, options2)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FIELD); + + const options3 = { + getFields: () => [123] // number instead of string + }; + expect(() => validateGetRequest(validGetRequest, options3)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FIELD); + }); + + // Test columnValues validation + test('should throw error when columnValues is not an array', () => { + const options = { + getColumnValues: () => 'not-an-array' + }; + expect(() => validateGetColumnRequest(validGetColumnRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_VALUES); + }); + + // Test columnValues array elements + test('should throw error when columnValues contain invalid elements', () => { + const options = { + getColumnValues: () => [null] + }; + expect(() => validateGetColumnRequest(validGetColumnRequest, options)) + .toThrow(SKYFLOW_ERROR_CODE.EMPTY_COLUMN_VALUE); + + const options2 = { + getColumnValues: () => [''] // empty string + }; + expect(() => validateGetColumnRequest(validGetColumnRequest, options2)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_VALUE); + + const options3 = { + getColumnValues: () => [123] // number instead of string + }; + expect(() => validateGetColumnRequest(validGetColumnRequest, options3)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_COLUMN_VALUE); + }); + + // Test valid options + test('should accept valid options with all properties', () => { + const options = { + getReturnTokens: () => true, + getRedactionType: () => 'REDACTED', + getOffset: () => '0', + getLimit: () => '10', + getDownloadURL: () => false, + getColumnName: () => 'column1', + getOrderBy: () => OrderByEnum.ASCENDING, + getFields: () => ['field1', 'field2'], + getColumnValues: () => ['value1', 'value2'] + }; + expect(() => validateGetRequest(validGetRequest, options)).not.toThrow(); + expect(() => validateGetColumnRequest(validGetColumnRequest, options)).not.toThrow(); + }); + + // Test partial valid options + test('should accept partial valid options', () => { + const options = { + getReturnTokens: () => true, + getRedactionType: () => 'REDACTED' + }; + expect(() => validateGetRequest(validGetRequest, options)).not.toThrow(); + expect(() => validateGetColumnRequest(validGetColumnRequest, options)).not.toThrow(); + }); + + // Test undefined options + test('should accept undefined options', () => { + expect(() => validateGetRequest(validGetRequest)).not.toThrow(); + expect(() => validateGetColumnRequest(validGetColumnRequest)).not.toThrow(); + }); +}); + +describe('validateCredentialsWithId', () => { + const type = 'vault'; + const typeId = 'vault_id'; + const id = 'test-id'; + + // Test PathCredentials validation + describe('PathCredentials validation', () => { + test('should throw error when path credentials are invalid', () => { + const credentials = { + path: 123, // invalid type + roles: ['role1'] + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_FILE_PATH_WITH_ID); + }); + + test('should throw error when roles is not an array', () => { + const credentials = { + path: '/valid/path', + roles: 'not-an-array' + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_ROLES_KEY_TYPE); + }); + + test('should throw error when context is not a string', () => { + const credentials = { + path: '/valid/path', + roles: ['role1'], + context: 123 // invalid type + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_CONTEXT); + }); + + test('should accept valid path credentials', () => { + // Mock fs.existsSync to return true + jest.spyOn(fs, 'existsSync').mockReturnValue(true); + + const credentials = { + path: '/valid/path', + roles: ['role1'], + context: 'test-context' + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)).not.toThrow(); + }); + }); + + // Test TokenCredentials validation + describe('TokenCredentials validation', () => { + test('should throw error when token is invalid', () => { + const credentials = { + token: 123 // invalid type + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_BEARER_TOKEN_WITH_ID); + }); + + test('should throw error when token is expired', () => { + // Mock isExpired to return true + jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(true); + + const credentials = { + token: 'expired-token' + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_BEARER_TOKEN_WITH_ID); + }); + + test('should accept valid token credentials', () => { + // Mock isExpired to return false + jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(false); + + const credentials = { + token: 'valid-token' + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)).not.toThrow(); + }); + }); + + // Test StringCredentials validation + describe('StringCredentials validation', () => { + test('should throw error when credentials string is invalid', () => { + const credentials = { + credentialsString: 123 // invalid type + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_PARSED_CREDENTIALS_STRING_WITH_ID); + }); + + test('should throw error when roles is not an array', () => { + const credentials = { + credentialsString: JSON.stringify({ + clientID: 'client1', + keyID: 'key1' + }), + roles: 'not-an-array' + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_ROLES_KEY_TYPE); + }); + + test('should throw error when context is not a string', () => { + const credentials = { + credentialsString: JSON.stringify({ + clientID: 'client1', + keyID: 'key1' + }), + roles: ['role1'], + context: 123 // invalid type + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_CONTEXT); + }); + + test('should accept valid string credentials', () => { + const credentials = { + credentialsString: JSON.stringify({ + clientID: 'client1', + keyID: 'key1' + }), + roles: ['role1'], + context: 'test-context' + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)).not.toThrow(); + }); + }); + + // Test multiple credentials error + test('should throw error when multiple credential types are provided', () => { + const credentials = { + token: 'valid-token', + path: '/valid/path' + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.MULTIPLE_CREDENTIALS_PASSED_WITH_ID); + }); + + // Test no credentials error + test('should throw error when no valid credential type is provided', () => { + const credentials = { + invalidType: 'some-value' + }; + expect(() => validateCredentialsWithId(credentials, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_CREDENTIALS_WITH_ID); + }); + + // Test null credentials + test('should throw error when credentials is null', () => { + expect(() => validateCredentialsWithId(null, type, typeId, id)) + .toThrow(SKYFLOW_ERROR_CODE.INVALID_CREDENTIALS_WITH_ID); + }); +}); \ No newline at end of file diff --git a/test/vault/controller/vault.test.js b/test/vault/controller/vault.test.js index eb27e8ec..03a3cf6c 100644 --- a/test/vault/controller/vault.test.js +++ b/test/vault/controller/vault.test.js @@ -156,7 +156,7 @@ describe('VaultController insert method', () => { test('should successfully insert records with bulk insert', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(false), @@ -180,7 +180,7 @@ describe('VaultController insert method', () => { expect(mockVaultClient.vaultAPI.recordServiceInsertRecord).toHaveBeenCalledWith( mockVaultClient.vaultId, - mockRequest.tableName, + mockRequest.table, expect.any(Object), // Request body expect.any(Object) // Headers ); @@ -191,7 +191,7 @@ describe('VaultController insert method', () => { test('should reject insert records with bulk insert', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(false), @@ -213,7 +213,7 @@ describe('VaultController insert method', () => { expect(mockVaultClient.vaultAPI.recordServiceInsertRecord).toHaveBeenCalledWith( mockVaultClient.vaultId, - mockRequest.tableName, + mockRequest.table, expect.any(Object), // Request body expect.any(Object) // Headers ); @@ -224,7 +224,7 @@ describe('VaultController insert method', () => { test('should successfully insert records with batch insert', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(true), @@ -253,7 +253,7 @@ describe('VaultController insert method', () => { test('should successfully insert records with batch insert with null record', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(true), @@ -282,7 +282,7 @@ describe('VaultController insert method', () => { test('should successfully insert records with batch insert with null response', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(true), @@ -308,7 +308,7 @@ describe('VaultController insert method', () => { test('should reject insert records with batch insert', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(true), @@ -336,7 +336,7 @@ describe('VaultController insert method', () => { test('should handle validation errors', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(false), @@ -358,7 +358,7 @@ describe('VaultController insert method', () => { test('should handle errors in the insert second Promise chain', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(false), @@ -380,7 +380,7 @@ describe('VaultController insert method', () => { test('should log and reject on API error', async () => { const mockRequest = { data: [{ field1: 'value1' }], - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getContinueOnError: jest.fn().mockReturnValue(false), @@ -732,7 +732,7 @@ describe('VaultController delete method', () => { test('should successfully delete records', async () => { const mockRequest = { deleteIds: ['id123'], - tableName: 'testTable', + table: 'testTable', }; const mockResponseData = { RecordIDResponse: ['id123'] }; @@ -747,7 +747,7 @@ describe('VaultController delete method', () => { expect(mockVaultClient.vaultAPI.recordServiceBulkDeleteRecord).toHaveBeenCalledWith( mockVaultClient.vaultId, - mockRequest.tableName, + mockRequest.table, expect.any(Object), // Request body expect.any(Object) // Headers ); @@ -759,7 +759,7 @@ describe('VaultController delete method', () => { test('should handle delete validation errors', async () => { const mockRequest = { deleteIds: ['id123'], - tableName: 'testTable', + table: 'testTable', }; validateDeleteRequest.mockImplementation(() => { @@ -774,7 +774,7 @@ describe('VaultController delete method', () => { test('should handle API errors during delete', async () => { const mockRequest = { deleteIds: ['id123'], - tableName: 'testTable', + table: 'testTable', }; const errorResponse = new Error('Invalid'); validateDeleteRequest.mockImplementation(() => { @@ -793,7 +793,7 @@ describe('VaultController delete method', () => { test('should reject when API returns no deleted records', async () => { const mockRequest = { deleteIds: ['id123'], - tableName: 'testTable', + table: 'testTable', }; const mockResponseData = { RecordIDResponse: [] }; // Simulate no records deleted validateDeleteRequest.mockImplementation(() => { @@ -815,7 +815,7 @@ describe('VaultController delete method', () => { test('should log and reject when API returns errors during delete', async () => { const mockRequest = { deleteIds: ['id123'], - tableName: 'testTable', + table: 'testTable', }; const errorResponse = new Error('Validation error'); validateDeleteRequest.mockImplementation(() => { @@ -1112,7 +1112,7 @@ describe('VaultController update method', () => { const skyflowId = 'id123'; const mockRequest = { data: { field1: 'value1', skyflowId }, - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getReturnTokens: jest.fn().mockReturnValue(true), @@ -1132,7 +1132,7 @@ describe('VaultController update method', () => { expect(mockVaultClient.vaultAPI.recordServiceUpdateRecord).toHaveBeenCalledWith( mockVaultClient.vaultId, - mockRequest.tableName, + mockRequest.table, skyflowId, expect.any(Object), // Update data expect.any(Object) // Headers @@ -1147,7 +1147,7 @@ describe('VaultController update method', () => { const skyflowId = 'id123'; const mockRequest = { data: { field1: 'value1', skyflowId }, - tableName: 'testTable', + table: 'testTable', }; const mockOptions = null; const mockResponseData = { skyflow_id: 'id123', tokens: { field1: 'token123' } }; @@ -1163,7 +1163,7 @@ describe('VaultController update method', () => { expect(mockVaultClient.vaultAPI.recordServiceUpdateRecord).toHaveBeenCalledWith( mockVaultClient.vaultId, - mockRequest.tableName, + mockRequest.table, skyflowId, expect.any(Object), // Update data expect.any(Object) // Headers @@ -1177,7 +1177,7 @@ describe('VaultController update method', () => { const skyflowId = 'id123'; const mockRequest = { data: { field1: 'value1', skyflowId }, - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getReturnTokens: jest.fn().mockReturnValue(true), @@ -1197,7 +1197,7 @@ describe('VaultController update method', () => { expect(mockVaultClient.vaultAPI.recordServiceUpdateRecord).toHaveBeenCalledWith( mockVaultClient.vaultId, - mockRequest.tableName, + mockRequest.table, skyflowId, expect.any(Object), // Update data expect.any(Object) // Headers @@ -1211,7 +1211,7 @@ describe('VaultController update method', () => { test('should handle validation errors', async () => { const mockRequest = { data: { field1: 'value1', skyflowId: 'id123' }, - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getReturnTokens: jest.fn().mockReturnValue(true), @@ -1231,7 +1231,7 @@ describe('VaultController update method', () => { test('should handle API errors during record update', async () => { const mockRequest = { data: { field1: 'value1', skyflowId: 'id123' }, - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getReturnTokens: jest.fn().mockReturnValue(true), @@ -1254,7 +1254,7 @@ describe('VaultController update method', () => { test('should return updated record without tokens when token mode is disabled', async () => { const mockRequest = { data: { field1: 'value1', skyflowId: 'id123' }, - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getReturnTokens: jest.fn().mockReturnValue(false), @@ -1280,7 +1280,7 @@ describe('VaultController update method', () => { test('should reject and log when API returns error during update', async () => { const mockRequest = { data: { field1: 'value1', skyflowId: 'id123' }, - tableName: 'testTable', + table: 'testTable', }; const mockOptions = { getReturnTokens: jest.fn().mockReturnValue(true), @@ -1311,7 +1311,7 @@ describe('VaultController uploadFile method', () => { mockVaultClient = { getLogLevel: jest.fn().mockReturnValue('DEBUG'), vaultAPI: { - fileServiceUploadFile: jest.fn(), + uploadFileV2: jest.fn(), }, initAPI: jest.fn(), getCredentials: jest.fn().mockReturnValue({}), @@ -1326,7 +1326,7 @@ describe('VaultController uploadFile method', () => { test('should successfully upload file using filePath', async () => { const mockRequest = { - tableName: 'testTable', + table: 'testTable', skyflowId: 'id123', columnName: 'testColumn', }; @@ -1341,9 +1341,9 @@ describe('VaultController uploadFile method', () => { jest.spyOn(mockFs, 'readFileSync').mockReturnValueOnce(mockFileBuffer); jest.spyOn(mockPath, 'basename').mockReturnValueOnce(mockFileName); - const mockResponseData = { skyflow_id: 'id123' }; + const mockResponseData = { skyflowID: 'id123' }; - mockVaultClient.vaultAPI.fileServiceUploadFile.mockImplementation(() => ({ + mockVaultClient.vaultAPI.uploadFileV2.mockImplementation(() => ({ withRawResponse: jest.fn().mockResolvedValueOnce({ data: mockResponseData, rawResponse: { headers: { get: jest.fn().mockReturnValue('request-id-123') } } @@ -1352,7 +1352,7 @@ describe('VaultController uploadFile method', () => { const response = await vaultController.uploadFile(mockRequest, mockOptions); - expect(mockVaultClient.vaultAPI.fileServiceUploadFile).toHaveBeenCalled(); + expect(mockVaultClient.vaultAPI.uploadFileV2).toHaveBeenCalled(); expect(response).toBeInstanceOf(FileUploadResponse); expect(response.skyflowId).toBe('id123'); expect(response.errors).toBeNull(); @@ -1360,7 +1360,7 @@ describe('VaultController uploadFile method', () => { test('should successfully upload file using base64', async () => { const mockRequest = { - tableName: 'testTable', + table: 'testTable', skyflowId: 'id123', columnName: 'testColumn', }; @@ -1371,8 +1371,8 @@ describe('VaultController uploadFile method', () => { getFileName: jest.fn().mockReturnValue('file.json'), }; const mockBuffer = Buffer.from('base64string', 'base64'); - const mockResponseData = { skyflow_id: 'id123' }; - mockVaultClient.vaultAPI.fileServiceUploadFile.mockImplementation(() => ({ + const mockResponseData = { skyflowID: 'id123' }; + mockVaultClient.vaultAPI.uploadFileV2.mockImplementation(() => ({ withRawResponse: jest.fn().mockResolvedValueOnce({ data: mockResponseData, rawResponse: { headers: { get: jest.fn().mockReturnValue('request-id-123') } } @@ -1381,7 +1381,7 @@ describe('VaultController uploadFile method', () => { const response = await vaultController.uploadFile(mockRequest, mockOptions); - expect(mockVaultClient.vaultAPI.fileServiceUploadFile).toHaveBeenCalled(); + expect(mockVaultClient.vaultAPI.uploadFileV2).toHaveBeenCalled(); expect(response).toBeInstanceOf(FileUploadResponse); expect(response.skyflowId).toBe('id123'); expect(response.errors).toBeNull(); @@ -1389,7 +1389,7 @@ describe('VaultController uploadFile method', () => { test('should successfully upload file using fileObject', async () => { const mockRequest = { - tableName: 'testTable', + table: 'testTable', skyflowId: 'id123', columnName: 'testColumn', }; @@ -1400,8 +1400,8 @@ describe('VaultController uploadFile method', () => { getFileObject: jest.fn().mockReturnValue(mockFileObject), getFileName: jest.fn(), }; - const mockResponseData = { skyflow_id: 'id123' }; - mockVaultClient.vaultAPI.fileServiceUploadFile.mockImplementation(() => ({ + const mockResponseData = { skyflowID: 'id123' }; + mockVaultClient.vaultAPI.uploadFileV2.mockImplementation(() => ({ withRawResponse: jest.fn().mockResolvedValueOnce({ data: mockResponseData, rawResponse: { headers: { get: jest.fn().mockReturnValue('request-id-123') } } @@ -1410,7 +1410,7 @@ describe('VaultController uploadFile method', () => { const response = await vaultController.uploadFile(mockRequest, mockOptions); - expect(mockVaultClient.vaultAPI.fileServiceUploadFile).toHaveBeenCalled(); + expect(mockVaultClient.vaultAPI.uploadFileV2).toHaveBeenCalled(); expect(response).toBeInstanceOf(FileUploadResponse); expect(response.skyflowId).toBe('id123'); expect(response.errors).toBeNull(); @@ -1418,7 +1418,7 @@ describe('VaultController uploadFile method', () => { test('should handle validation errors during upload', async () => { const mockRequest = { - tableName: 'testTable', + table: 'testTable', skyflowId: 'id123', columnName: 'testColumn', }; @@ -1435,12 +1435,12 @@ describe('VaultController uploadFile method', () => { await expect(vaultController.uploadFile(mockRequest, mockOptions)).rejects.toThrow('Validation error'); expect(validateUploadFileRequest).toHaveBeenCalled(); - expect(mockVaultClient.vaultAPI.fileServiceUploadFile).not.toHaveBeenCalled(); + expect(mockVaultClient.vaultAPI.uploadFileV2).not.toHaveBeenCalled(); }); test('should handle API errors during file upload', async () => { const mockRequest = { - tableName: 'testTable', + table: 'testTable', skyflowId: 'id123', columnName: 'testColumn', }; @@ -1452,7 +1452,7 @@ describe('VaultController uploadFile method', () => { }; const mockFileBuffer = Buffer.from('file content'); jest.spyOn(mockFs, 'readFileSync').mockReturnValueOnce(mockFileBuffer); - mockVaultClient.vaultAPI.fileServiceUploadFile.mockImplementation(() => { + mockVaultClient.vaultAPI.uploadFileV2.mockImplementation(() => { return { withRawResponse: jest.fn().mockRejectedValue(new Error('API error')), }; @@ -1689,12 +1689,12 @@ describe('VaultController Error Handling', () => { }; }); - await expect(vaultController.insert({ tableName: 'users', data: [{}] })).rejects.toThrow(SkyflowError); + await expect(vaultController.insert({ table: 'users', data: [{}] })).rejects.toThrow(SkyflowError); // You can check if the error contains the right message try { - await vaultController.insert({ tableName: 'users', data: [{}] }); + await vaultController.insert({ table: 'users', data: [{}] }); } catch (e) { expect(e).toBeDefined(); } @@ -1722,11 +1722,11 @@ describe('VaultController Error Handling', () => { }; }); - await expect(vaultController.insert({ tableName: 'users', data: [{}] })).rejects.toThrow(SkyflowError); + await expect(vaultController.insert({ table: 'users', data: [{}] })).rejects.toThrow(SkyflowError); // Check that the error message is as expected try { - await vaultController.insert({ tableName: 'users', data: [{}] }); + await vaultController.insert({ table: 'users', data: [{}] }); } catch (e) { expect(e).toBeDefined(); } @@ -1751,11 +1751,11 @@ describe('VaultController Error Handling', () => { }; }); - await expect(vaultController.insert({ tableName: 'users', data: [{}] })).rejects.toThrow(SkyflowError); + await expect(vaultController.insert({ table: 'users', data: [{}] })).rejects.toThrow(SkyflowError); // Check that the error message is as expected try { - await vaultController.insert({ tableName: 'users', data: [{}] }); + await vaultController.insert({ table: 'users', data: [{}] }); } catch (e) { expect(e).toBeDefined(); } @@ -1774,11 +1774,11 @@ describe('VaultController Error Handling', () => { }; }); - await expect(vaultController.insert({ tableName: 'users', data: [{}] })).rejects.toThrow(SkyflowError); + await expect(vaultController.insert({ table: 'users', data: [{}] })).rejects.toThrow(SkyflowError); // Check that the error message is as expected try { - await vaultController.insert({ tableName: 'users', data: [{}] }); + await vaultController.insert({ table: 'users', data: [{}] }); } catch (e) { expect(e).toBeDefined(); }