diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index 383983316f..83cbed66df 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -44,6 +44,10 @@ assign_issues_by: - "api: parametermanager" to: - GoogleCloudPlatform/cloud-parameters-team +- labels: + - "api: modelarmor" + to: + - GoogleCloudPlatform/cloud-modelarmor-team assign_prs_by: - labels: @@ -77,3 +81,7 @@ assign_prs_by: - "api: parametermanager" to: - GoogleCloudPlatform/cloud-parameters-team +- labels: + - "api: modelarmor" + to: + - GoogleCloudPlatform/cloud-modelarmor-team diff --git a/CODEOWNERS b/CODEOWNERS index e2e4a75e55..bba35081d8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -44,6 +44,7 @@ document-warehouse @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPla ai-platform @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/text-embedding @GoogleCloudPlatform/cloud-samples-reviewers asset @GoogleCloudPlatform/cloud-asset-analysis-team @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers dlp @GoogleCloudPlatform/googleapis-dlp @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers +model-armor @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers @GoogleCloudPlatform/cloud-modelarmor-team security-center @GoogleCloudPlatform/gcp-security-command-center @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers retail @GoogleCloudPlatform/cloud-retail-team @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers media @GoogleCloudPlatform/cloud-media-team @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers diff --git a/model-armor/package.json b/model-armor/package.json new file mode 100644 index 0000000000..40abac10c5 --- /dev/null +++ b/model-armor/package.json @@ -0,0 +1,26 @@ +{ + "name": "nodejs-model-armor-samples", + "private": true, + "license": "Apache-2.0", + "files": [ + "*.js" + ], + "author": "Google LLC", + "repository": "googleapis/nodejs-model-armor", + "engines": { + "node": ">=16.0.0" + }, + "scripts": { + "test": "c8 mocha -p -j 2 --recursive test/ --timeout=60000" + }, + "dependencies": { + "@google-cloud/modelarmor": "^0.1.0" + }, + "devDependencies": { + "c8": "^10.0.0", + "chai": "^4.5.0", + "mocha": "^10.0.0", + "uuid": "^10.0.0" + } +} + \ No newline at end of file diff --git a/model-armor/snippets/getFolderFloorSettings.js b/model-armor/snippets/getFolderFloorSettings.js new file mode 100644 index 0000000000..d0233bf379 --- /dev/null +++ b/model-armor/snippets/getFolderFloorSettings.js @@ -0,0 +1,52 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +/** + * Retrieves the floor settings for a Google Cloud folder. + * + * @param {string} folderId - The ID of the Google Cloud folder for which to retrieve floor settings. + */ +async function main(folderId) { + // [START modelarmor_get_folder_floor_settings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const folderId = 'your-folder-id'; + + const name = `folders/${folderId}/locations/global/floorSetting`; + + // Imports the Modelarmor library + const {ModelArmorClient} = require('@google-cloud/modelarmor').v1; + + // Instantiates a client + const modelarmorClient = new ModelArmorClient(); + + async function getFolderFloorSettings() { + // Construct request + const request = { + name, + }; + + const response = await modelarmorClient.getFloorSetting(request); + console.log(response); + } + + getFolderFloorSettings(); + // [END modelarmor_get_folder_floor_settings] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/model-armor/snippets/getOrganizationFloorSettings.js b/model-armor/snippets/getOrganizationFloorSettings.js new file mode 100644 index 0000000000..e0c07ad5f7 --- /dev/null +++ b/model-armor/snippets/getOrganizationFloorSettings.js @@ -0,0 +1,54 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +/** + * Retrieves the floor settings for a Google Cloud organization. + * + * @param {string} organizationId - The ID of the Google Cloud organization for which to retrieve + * floor settings. + */ +async function main(organizationId) { + // [START modelarmor_get_organization_floor_settings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const organizationId = 'your-organization-id'; + + const name = `organizations/${organizationId}/locations/global/floorSetting`; + + // Imports the Modelarmor library + const {ModelArmorClient} = require('@google-cloud/modelarmor').v1; + + // Instantiates a client + const modelarmorClient = new ModelArmorClient(); + + async function getOrganizationFloorSettings() { + // Construct request + const request = { + name, + }; + + // Run request + const response = await modelarmorClient.getFloorSetting(request); + console.log(response); + } + + getOrganizationFloorSettings(); + // [END modelarmor_get_organization_floor_settings] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/model-armor/snippets/getProjectFloorSettings.js b/model-armor/snippets/getProjectFloorSettings.js new file mode 100644 index 0000000000..e8922afba3 --- /dev/null +++ b/model-armor/snippets/getProjectFloorSettings.js @@ -0,0 +1,54 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +/** + * Retrieves the floor settings for a Google Cloud project. + * + * @param {string} projectId - The ID of the Google Cloud project for which to retrieve + * floor settings. + */ +async function main(projectId) { + // [START modelarmor_get_project_floor_settings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'your-project-id'; + + const name = `projects/${projectId}/locations/global/floorSetting`; + + // Imports the Modelarmor library + const {ModelArmorClient} = require('@google-cloud/modelarmor').v1; + + // Instantiates a client + const modelarmorClient = new ModelArmorClient(); + + async function getProjectFloorSettings() { + // Construct request + const request = { + name, + }; + + // Run request + const response = await modelarmorClient.getFloorSetting(request); + console.log(response); + } + + getProjectFloorSettings(); + // [END modelarmor_get_project_floor_settings] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/model-armor/snippets/updateFolderFloorSettings.js b/model-armor/snippets/updateFolderFloorSettings.js new file mode 100644 index 0000000000..46ebb1bfb8 --- /dev/null +++ b/model-armor/snippets/updateFolderFloorSettings.js @@ -0,0 +1,73 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +/** + * Updates the floor settings of a folder in Model Armor. + * + * @param {string} folderId - Google Cloud folder ID for which floor settings need to be updated. + */ +async function main(folderId) { + // [START modelarmor_update_folder_floor_settings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const folderId = 'your-folder-id'; + + // Imports the Model Armor library + const modelarmor = require('@google-cloud/modelarmor'); + const {ModelArmorClient} = modelarmor.v1; + const {protos} = modelarmor; + + // Instantiates a client + const client = new ModelArmorClient(); + + async function updateFolderFloorSettings() { + const floorSettingsName = `folders/${folderId}/locations/global/floorSetting`; + + // Build the floor settings with your preferred filters + // For more details on filters, please refer to the following doc: + // https://cloud.google.com/security-command-center/docs/key-concepts-model-armor#ma-filters + const floorSetting = { + name: floorSettingsName, + filterConfig: { + raiSettings: { + raiFilters: [ + { + filterType: + protos.google.cloud.modelarmor.v1.RaiFilterType.HATE_SPEECH, + confidenceLevel: + protos.google.cloud.modelarmor.v1.DetectionConfidenceLevel.HIGH, + }, + ], + }, + }, + enableFloorSettingEnforcement: true, + }; + + const request = { + floorSetting: floorSetting, + }; + + const [response] = await client.updateFloorSetting(request); + console.log('Updated folder floor settings', response); + } + + updateFolderFloorSettings(); + // [END modelarmor_update_folder_floor_settings] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/model-armor/snippets/updateOrganizationFloorSettings.js b/model-armor/snippets/updateOrganizationFloorSettings.js new file mode 100644 index 0000000000..1625ed2cd8 --- /dev/null +++ b/model-armor/snippets/updateOrganizationFloorSettings.js @@ -0,0 +1,68 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +/** + * Updates the floor settings of an organization in Model Armor. + * + * @param {string} organizationId - Google Cloud organization ID for which floor settings need to be updated. + */ +async function main(organizationId) { + // [START modelarmor_update_organization_floor_settings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const organizationId = 'your-organization-id'; + + const modelarmor = require('@google-cloud/modelarmor'); + const {ModelArmorClient} = modelarmor.v1; + const {protos} = modelarmor; + + const client = new ModelArmorClient(); + + const floorSettingsName = `organizations/${organizationId}/locations/global/floorSetting`; + + // Build the floor settings with your preferred filters + // For more details on filters, please refer to the following doc: + // https://cloud.google.com/security-command-center/docs/key-concepts-model-armor#ma-filters + const floorSetting = { + name: floorSettingsName, + filterConfig: { + raiSettings: { + raiFilters: [ + { + filterType: + protos.google.cloud.modelarmor.v1.RaiFilterType.HATE_SPEECH, + confidenceLevel: + protos.google.cloud.modelarmor.v1.DetectionConfidenceLevel.HIGH, + }, + ], + }, + }, + enableFloorSettingEnforcement: true, + }; + + const request = { + floorSetting: floorSetting, + }; + + const [response] = await client.updateFloorSetting(request); + console.log('Updated organization floor settings:', response); + + // [END modelarmor_update_organization_floor_settings] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/model-armor/snippets/updateProjectFloorSettings.js b/model-armor/snippets/updateProjectFloorSettings.js new file mode 100644 index 0000000000..b8787a2a7d --- /dev/null +++ b/model-armor/snippets/updateProjectFloorSettings.js @@ -0,0 +1,67 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +/** + * Updates the floor settings of a project in Model Armor. + * + * @param {string} projectId - Google Cloud project ID for which floor settings need to be updated. + */ +async function main(projectId) { + // [START modelarmor_update_project_floor_settings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'your-project-id'; + + const modelarmor = require('@google-cloud/modelarmor'); + const {ModelArmorClient} = modelarmor.v1; + const {protos} = modelarmor; + + const client = new ModelArmorClient(); + + const floorSettingsName = `projects/${projectId}/locations/global/floorSetting`; + + // Build the floor settings with your preferred filters + // For more details on filters, please refer to the following doc: + // https://cloud.google.com/security-command-center/docs/key-concepts-model-armor#ma-filters + const floorSetting = { + name: floorSettingsName, + filterConfig: { + raiSettings: { + raiFilters: [ + { + filterType: + protos.google.cloud.modelarmor.v1.RaiFilterType.HATE_SPEECH, + confidenceLevel: + protos.google.cloud.modelarmor.v1.DetectionConfidenceLevel.HIGH, + }, + ], + }, + }, + enableFloorSettingEnforcement: true, + }; + + const request = { + floorSetting: floorSetting, + }; + + const [response] = await client.updateFloorSetting(request); + console.log('Updated project floor settings:', response); + // [END modelarmor_update_project_floor_settings] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/model-armor/test/.eslintrc.yml b/model-armor/test/.eslintrc.yml new file mode 100644 index 0000000000..9351c489b5 --- /dev/null +++ b/model-armor/test/.eslintrc.yml @@ -0,0 +1,17 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +env: + mocha: true \ No newline at end of file diff --git a/model-armor/test/modelarmor.test.js b/model-armor/test/modelarmor.test.js new file mode 100644 index 0000000000..a3574fcfe4 --- /dev/null +++ b/model-armor/test/modelarmor.test.js @@ -0,0 +1,319 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const cp = require('child_process'); +const {v4: uuidv4} = require('uuid'); +const {ModelArmorClient} = require('@google-cloud/modelarmor').v1; + +let projectId; +const locationId = process.env.GCLOUD_LOCATION || 'us-central1'; +const folderId = process.env.MA_FOLDER_ID; +const organizationId = process.env.MA_ORG_ID; +const options = { + apiEndpoint: `modelarmor.${locationId}.rep.googleapis.com`, +}; + +const client = new ModelArmorClient(options); +const templateIdPrefix = `test-template-${uuidv4().substring(0, 8)}`; + +let emptyTemplateId; +let basicTemplateId; +let basicSdpTemplateId; +let templateToDeleteId; + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +// Helper function to create a template for sanitization tests +async function createTemplate(templateId, filterConfig) { + const parent = `projects/${projectId}/locations/${locationId}`; + + try { + const [response] = await client.createTemplate({ + parent: parent, + templateId: templateId, + template: { + filterConfig: filterConfig, + }, + }); + + console.log(`Created template: ${response.name}`); + return response; + } catch (error) { + console.error(`Error creating template ${templateId}:`, error); + throw error; + } +} + +// Helper function to delete a template +async function deleteTemplate(templateName) { + try { + await client.deleteTemplate({ + name: templateName, + }); + console.log(`Deleted template: ${templateName}`); + } catch (error) { + if (error.code === 5) { + // Not found + console.log(`Template ${templateName} was not found.`); + } else { + console.error(`Error deleting template ${templateName}:`, error); + } + } +} + +async function disableFloorSettings() { + try { + // Disable project floor settings + const [projectFloorSettings] = await client.getFloorSetting({ + name: `projects/${projectId}/locations/global/floorSetting`, + }); + + if (projectFloorSettings.enableFloorSettingEnforcement) { + const [updatedProjectSettings] = await client.updateFloorSetting({ + floorSetting: { + name: `projects/${projectId}/locations/global/floorSetting`, + enableFloorSettingEnforcement: false, + }, + updateMask: { + paths: ['enable_floor_setting_enforcement'], + }, + }); + console.log('Disabled project floor settings:', updatedProjectSettings.name); + } + + // Disable folder floor settings if folderId is available + if (folderId) { + const [folderFloorSettings] = await client.getFloorSetting({ + name: `folders/${folderId}/locations/global/floorSetting`, + }); + + if (folderFloorSettings.enableFloorSettingEnforcement) { + const [updatedFolderSettings] = await client.updateFloorSetting({ + floorSetting: { + name: `folders/${folderId}/locations/global/floorSetting`, + enableFloorSettingEnforcement: false, + }, + updateMask: { + paths: ['enable_floor_setting_enforcement'], + }, + }); + console.log('Disabled folder floor settings:', updatedFolderSettings.name); + } + } + + // Disable organization floor settings if organizationId is available + if (organizationId) { + const [orgFloorSettings] = await client.getFloorSetting({ + name: `organizations/${organizationId}/locations/global/floorSetting`, + }); + + if (orgFloorSettings.enableFloorSettingEnforcement) { + const [updatedOrgSettings] = await client.updateFloorSetting({ + floorSetting: { + name: `organizations/${organizationId}/locations/global/floorSetting`, + enableFloorSettingEnforcement: false, + }, + updateMask: { + paths: ['enable_floor_setting_enforcement'], + }, + }); + console.log('Disabled organization floor settings:', updatedOrgSettings.name); + } + } + } catch (error) { + console.error('Error disabling floor settings:', error); + } +} + +describe('Model Armor tests', () => { + const templatesToDelete = []; + + before(async () => { + projectId = await client.getProjectId(); + + // Import necessary enums + const {protos} = require('@google-cloud/modelarmor'); + const DetectionConfidenceLevel = + protos.google.cloud.modelarmor.v1.DetectionConfidenceLevel; + const PiAndJailbreakFilterEnforcement = + protos.google.cloud.modelarmor.v1.PiAndJailbreakFilterSettings + .PiAndJailbreakFilterEnforcement; + const MaliciousUriFilterEnforcement = + protos.google.cloud.modelarmor.v1.MaliciousUriFilterSettings + .MaliciousUriFilterEnforcement; + const RaiFilterType = protos.google.cloud.modelarmor.v1.RaiFilterType; + const SdpBasicConfigEnforcement = + protos.google.cloud.modelarmor.v1.SdpBasicConfig + .SdpBasicConfigEnforcement; + + // Create empty template for sanitizeUserPrompt tests + emptyTemplateId = `${templateIdPrefix}-empty`; + await createTemplate(emptyTemplateId, {}); + + // Create basic template with PI/Jailbreak and Malicious URI filters for sanitizeUserPrompt tests + basicTemplateId = `${templateIdPrefix}-basic`; + await createTemplate(basicTemplateId, { + piAndJailbreakFilterSettings: { + filterEnforcement: PiAndJailbreakFilterEnforcement.ENABLED, + confidenceLevel: DetectionConfidenceLevel.MEDIUM_AND_ABOVE, + }, + maliciousUriFilterSettings: { + filterEnforcement: MaliciousUriFilterEnforcement.ENABLED, + }, + }); + + // Create a template to be deleted + templateToDeleteId = `${templateIdPrefix}-to-delete`; + await createTemplate(templateToDeleteId, { + piAndJailbreakFilterSettings: { + filterEnforcement: PiAndJailbreakFilterEnforcement.ENABLED, + confidenceLevel: DetectionConfidenceLevel.MEDIUM_AND_ABOVE, + }, + maliciousUriFilterSettings: { + filterEnforcement: MaliciousUriFilterEnforcement.ENABLED, + }, + }); + + // Create a basic SDP template for testing + basicSdpTemplateId = `${templateIdPrefix}-basic-sdp`; + await createTemplate(basicSdpTemplateId, { + filterConfig: { + raiSettings: { + raiFilters: [ + { + filterType: RaiFilterType.DANGEROUS, + confidenceLevel: DetectionConfidenceLevel.HIGH, + }, + { + filterType: RaiFilterType.HARASSMENT, + confidenceLevel: DetectionConfidenceLevel.MEDIUM_AND_ABOVE, + }, + { + filterType: RaiFilterType.HATE_SPEECH, + confidenceLevel: DetectionConfidenceLevel.HIGH, + }, + { + filterType: RaiFilterType.SEXUALLY_EXPLICIT, + confidenceLevel: DetectionConfidenceLevel.HIGH, + }, + ], + }, + sdpSettings: { + basicConfig: { + filterEnforcement: SdpBasicConfigEnforcement.ENABLED, + }, + }, + }, + }); + + templatesToDelete.push( + `projects/${projectId}/locations/${locationId}/templates/${emptyTemplateId}`, + `projects/${projectId}/locations/${locationId}/templates/${basicTemplateId}`, + `projects/${projectId}/locations/${locationId}/templates/${basicSdpTemplateId}` + ); + }); + + after(async () => { + // Disable floor settings to restore original state + await disableFloorSettings(); + + // Clean up all templates + const directTemplates = [emptyTemplateId, basicTemplateId]; + for (const templateId of directTemplates) { + await deleteTemplate( + `projects/${projectId}/locations/${locationId}/templates/${templateId}` + ); + } + + for (const templateName of templatesToDelete) { + try { + await client.deleteTemplate({name: templateName}); + console.log(`Cleaned up template: ${templateName}`); + } catch (error) { + console.error(`Failed to delete template ${templateName}:`, error); + } + } + }); + + // =================== Floor Settings Tests =================== + + it('should get organization floor settings', () => { + const output = execSync( + `node snippets/getOrganizationFloorSettings.js ${organizationId}` + ).toString(); + + // Check for expected name format in output + const expectedName = `organizations/${organizationId}/locations/global/floorSetting`; + assert.match(output, new RegExp(expectedName.replace(/\//g, '\\/'))); + }); + + it('should get folder floor settings', () => { + const output = execSync( + `node snippets/getFolderFloorSettings.js ${folderId}` + ).toString(); + + // Check for expected name format in output + const expectedName = `folders/${folderId}/locations/global/floorSetting`; + assert.match(output, new RegExp(expectedName.replace(/\//g, '\\/'))); + }); + + it('should get project floor settings', () => { + const output = execSync( + `node snippets/getProjectFloorSettings.js ${projectId}` + ).toString(); + + // Check for expected name format in output + const expectedName = `projects/${projectId}/locations/global/floorSetting`; + assert.match(output, new RegExp(expectedName.replace(/\//g, '\\/'))); + }); + + it('should update organization floor settings', () => { + const output = execSync( + `node snippets/updateOrganizationFloorSettings.js ${organizationId}` + ).toString(); + + // Check that the update was performed + assert.match(output, /Updated organization floor settings/); + + // Check that the response contains enableFloorSettingEnforcement=true + assert.match(output, /enableFloorSettingEnforcement:\s*true/); + }); + + it('should update folder floor settings', () => { + const output = execSync( + `node snippets/updateFolderFloorSettings.js ${folderId}` + ).toString(); + + // Check that the update was performed + assert.match(output, /Updated folder floor settings/); + + // Check that the response contains enableFloorSettingEnforcement=true + assert.match(output, /enableFloorSettingEnforcement:\s*true/); + }); + + it('should update project floor settings', () => { + const output = execSync( + `node snippets/updateProjectFloorSettings.js ${projectId}` + ).toString(); + + // Check that the update was performed + assert.match(output, /Updated project floor settings/); + + // Check that the response contains enableFloorSettingEnforcement=true + assert.match(output, /enableFloorSettingEnforcement:\s*true/); + }); +}); \ No newline at end of file