From 6e1295a7e33fcddf25d6a35685b5e480169f3966 Mon Sep 17 00:00:00 2001 From: Saurabh Tripathi Date: Fri, 22 Aug 2025 07:09:35 +0200 Subject: [PATCH 1/2] New command: spo site versionpolicy get. Closes #6750 --- .devproxy/api-specs/sharepoint.yaml | 103 +++++++---- .../cmd/spo/site/site-versionpolicy-get.mdx | 97 ++++++++++ docs/src/config/sidebars.ts | 5 + src/m365/spo/commands.ts | 1 + .../site/site-versionpolicy-get.spec.ts | 174 ++++++++++++++++++ .../commands/site/site-versionpolicy-get.ts | 87 +++++++++ 6 files changed, 427 insertions(+), 40 deletions(-) create mode 100644 docs/docs/cmd/spo/site/site-versionpolicy-get.mdx create mode 100644 src/m365/spo/commands/site/site-versionpolicy-get.spec.ts create mode 100644 src/m365/spo/commands/site/site-versionpolicy-get.ts diff --git a/.devproxy/api-specs/sharepoint.yaml b/.devproxy/api-specs/sharepoint.yaml index 4887a0bd60f..d4f306d7653 100644 --- a/.devproxy/api-specs/sharepoint.yaml +++ b/.devproxy/api-specs/sharepoint.yaml @@ -61,10 +61,10 @@ paths: post: security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -72,10 +72,33 @@ paths: get: security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl + responses: + 200: + description: OK + /_api/site/VersionPolicyForNewLibrariesTemplate: + get: + parameters: + - name: $expand + in: query + required: false + schema: + type: string + example: VersionPolicies + security: + - delegated: + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl + - application: + - Sites.Read.All + - Sites.ReadWrite.All + - Sites.Manage.All + - Sites.FullControl.All responses: 200: description: OK @@ -83,10 +106,10 @@ paths: get: security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -102,9 +125,9 @@ paths: example: "'%2FShared%20Documents%2FMy%20Folder%20Name'" security: - delegated: - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -170,10 +193,10 @@ paths: example: "'%2FShared%20Documents'" security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -196,9 +219,9 @@ paths: example: "url='README.md',%20overwrite=true" security: - delegated: - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -206,15 +229,15 @@ paths: get: security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl - application: - - Sites.Read.All - - Sites.Manage.All - - Sites.ReadWrite.All - - Sites.FullControl.All + - Sites.Read.All + - Sites.Manage.All + - Sites.ReadWrite.All + - Sites.FullControl.All responses: 200: description: OK @@ -222,14 +245,14 @@ paths: get: security: - delegated: - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl - application: - - Sites.Read.All - - Sites.Manage.All - - Sites.ReadWrite.All - - Sites.FullControl.All + - Sites.Read.All + - Sites.Manage.All + - Sites.ReadWrite.All + - Sites.FullControl.All responses: 200: description: OK @@ -252,7 +275,7 @@ paths: example: "overwrite=false,%20url='spfx.sppkg'" security: - delegated: - - AllSites.FullControl + - AllSites.FullControl responses: 200: description: OK @@ -275,10 +298,10 @@ paths: example: "'f55e3c17-63ea-456a-8451-48d2839760f7'" security: - delegated: - - AllSites.FullControl + - AllSites.FullControl responses: 200: description: OK x-ms-generated-by: toolName: Dev Proxy - toolVersion: 0.25.0 \ No newline at end of file + toolVersion: 0.25.0 diff --git a/docs/docs/cmd/spo/site/site-versionpolicy-get.mdx b/docs/docs/cmd/spo/site/site-versionpolicy-get.mdx new file mode 100644 index 00000000000..f500333a96f --- /dev/null +++ b/docs/docs/cmd/spo/site/site-versionpolicy-get.mdx @@ -0,0 +1,97 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# spo site versionpolicy get + +Retrieves the version policy settings of a specific site. + +## Usage + +```sh +m365 spo site versionpolicy get [options] +``` + +## Options + +```md definition-list +`-u, --siteUrl ` +: URL of the site. +``` + + + +## Permissions + + + + + | Resource | Permissions | + |------------|---------------| + | SharePoint | AllSites.Read | + + + + + | Resource | Permissions | + |------------|----------------| + | SharePoint | Sites.Read.All | + + + + +## Examples + +Retrieve the version policy settings of a specific site. + +```sh +m365 spo site versionpolicy get --siteUrl "https://contoso.sharepoint.com/sites/Marketing" +``` + +## Response + + + + + ```json + { + "defaultTrimMode": "automatic", + "defaultExpireAfterDays": 30, + "majorVersionLimit": 500 + } + ``` + + + + + ```text + defaultExpireAfterDays: 30 + defaultTrimMode : automatic + majorVersionLimit : 500 + ``` + + + + + ```csv + defaultTrimMode,defaultExpireAfterDays,majorVersionLimit + automatic,30,500 + ``` + + + + + ```md + # spo site versionpolicy get --debug "false" --verbose "false" --siteUrl "https://contoso.sharepoint.com/" + + Date: 8/23/2025 + + Property | Value + ---------|------- + defaultTrimMode | automatic + defaultExpireAfterDays | 30 + majorVersionLimit | 500 + ``` + + + diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index f7e795494dc..1a28d3dcaa4 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -3808,6 +3808,11 @@ const sidebars: SidebarsConfig = { type: 'doc', label: 'site sharingpermission set', id: 'cmd/spo/site/site-sharingpermission-set' + }, + { + type: 'doc', + label: 'site versionpolicy get', + id: 'cmd/spo/site/site-versionpolicy-get' } ] }, diff --git a/src/m365/spo/commands.ts b/src/m365/spo/commands.ts index d526308c48c..2044d9703a4 100644 --- a/src/m365/spo/commands.ts +++ b/src/m365/spo/commands.ts @@ -286,6 +286,7 @@ export default { SITE_RENAME: `${prefix} site rename`, SITE_SET: `${prefix} site set`, SITE_SHARINGPERMISSION_SET: `${prefix} site sharingpermission set`, + SITE_VERSIONPOLICY_GET: `${prefix} site versionpolicy get`, SITE_CHROME_SET: `${prefix} site chrome set`, SITE_UNARCHIVE: `${prefix} site unarchive`, SITEDESIGN_ADD: `${prefix} sitedesign add`, diff --git a/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts b/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts new file mode 100644 index 00000000000..962e73509f4 --- /dev/null +++ b/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts @@ -0,0 +1,174 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import { z } from 'zod'; +import commands from '../../commands.js'; +import command from './site-versionpolicy-get.js'; + +describe(commands.SITE_VERSIONPOLICY_GET, () => { + let log: any[]; + let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; + let commandInfo: CommandInfo; + let commandOptionsSchema: z.ZodTypeAny; + const validSiteUrl = "https://contoso.sharepoint.com"; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').resolves(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + commandInfo = cli.getCommandInfo(command); + commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + auth.connection.active = true; + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + }); + + afterEach(() => { + sinonUtil.restore([ + request.get + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.SITE_VERSIONPOLICY_GET); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('fails validation if site URL is not a valid URL', async () => { + const actual = commandOptionsSchema.safeParse({ siteUrl: 'foo' }); + assert.strictEqual(actual.success, false); + }); + + it('passes validation if valid site URL is specified', async () => { + const actual = await command.validate({ options: { siteUrl: validSiteUrl } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('retrieves "age" version policy settings for the specified site', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${validSiteUrl}/_api/site/VersionPolicyForNewLibrariesTemplate?$expand=VersionPolicies`) { + return { + VersionPolicies: { + DefaultTrimMode: 1, + DefaultExpireAfterDays: 200 + }, + MajorVersionLimit: 100 + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { siteUrl: validSiteUrl } }); + assert(loggerLogSpy.calledWith({ + defaultTrimMode: 'age', + defaultExpireAfterDays: 200, + majorVersionLimit: 100 + })); + }); + + it('retrieves "automatic" version policy settings for the specified site', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${validSiteUrl}/_api/site/VersionPolicyForNewLibrariesTemplate?$expand=VersionPolicies`) { + return { + VersionPolicies: { + DefaultTrimMode: 2, + DefaultExpireAfterDays: 30 + }, + MajorVersionLimit: 500 + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { siteUrl: validSiteUrl } }); + assert(loggerLogSpy.calledWith({ + defaultTrimMode: 'automatic', + defaultExpireAfterDays: 30, + majorVersionLimit: 500 + })); + }); + + it('retrieves "number" version policy settings for the specified site', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${validSiteUrl}/_api/site/VersionPolicyForNewLibrariesTemplate?$expand=VersionPolicies`) { + return { + VersionPolicies: { + DefaultTrimMode: 0, + DefaultExpireAfterDays: 0 + }, + MajorVersionLimit: 300 + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { siteUrl: validSiteUrl, verbose: true } }); + assert(loggerLogSpy.calledWith({ + defaultTrimMode: 'number', + defaultExpireAfterDays: 0, + majorVersionLimit: 300 + })); + }); + + it('retrieves "inheritTenant" version policy settings for the specified site', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${validSiteUrl}/_api/site/VersionPolicyForNewLibrariesTemplate?$expand=VersionPolicies`) { + return { + MajorVersionLimit: -1 + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { siteUrl: validSiteUrl, verbose: true } }); + assert(loggerLogSpy.calledWith({ + defaultTrimMode: 'inheritTenant', + defaultExpireAfterDays: null, + majorVersionLimit: -1 + })); + }); + + it('correctly handles API OData error', async () => { + sinon.stub(request, 'get').rejects(new Error('An error has occurred')); + + await assert.rejects(command.action(logger, { options: { siteUrl: validSiteUrl } }), + new CommandError('An error has occurred')); + }); +}); \ No newline at end of file diff --git a/src/m365/spo/commands/site/site-versionpolicy-get.ts b/src/m365/spo/commands/site/site-versionpolicy-get.ts new file mode 100644 index 00000000000..877eb808c67 --- /dev/null +++ b/src/m365/spo/commands/site/site-versionpolicy-get.ts @@ -0,0 +1,87 @@ +import commands from '../../commands.js'; +import { Logger } from '../../../../cli/Logger.js'; +import SpoCommand from '../../../base/SpoCommand.js'; +import { globalOptionsZod } from '../../../../Command.js'; +import { z } from 'zod'; +import { zod } from '../../../../utils/zod.js'; +import { validation } from '../../../../utils/validation.js'; +import request, { CliRequestOptions } from '../../../../request.js'; + +export const options = globalOptionsZod + .extend({ + siteUrl: zod.alias('u', z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ + message: `'${url}' is not a valid SharePoint Online site URL.` + })) + ) + }) + .strict(); + +declare type Options = z.infer; + +interface CommandArgs { + options: Options; +} + +class SpoSiteVersionpolicyGetCommand extends SpoCommand { + public get name(): string { + return commands.SITE_VERSIONPOLICY_GET; + } + + public get description(): string { + return 'Retrieves the version policy settings of a specific site'; + } + + public get schema(): z.ZodTypeAny | undefined { + return options; + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + if (this.verbose) { + await logger.logToStderr(`Retrieving version policy settings for site '${args.options.siteUrl}'...`); + } + + try { + const requestOptions: CliRequestOptions = { + url: `${args.options.siteUrl}/_api/site/VersionPolicyForNewLibrariesTemplate?$expand=VersionPolicies`, + headers: { + accept: 'application/json;odata=nometadata' + }, + responseType: 'json' + }; + + const response = await request.get(requestOptions); + + let defaultTrimMode: string = 'number'; + if (response.MajorVersionLimit === -1) { + defaultTrimMode = 'inheritTenant'; + } + else if (response.VersionPolicies) { + switch (response.VersionPolicies.DefaultTrimMode) { + case 1: + defaultTrimMode = 'age'; + break; + case 2: + defaultTrimMode = 'automatic'; + break; + case 0: + default: + defaultTrimMode = 'number'; + } + } + + const output = { + defaultTrimMode: defaultTrimMode, + defaultExpireAfterDays: response.VersionPolicies?.DefaultExpireAfterDays ?? null, + majorVersionLimit: response.MajorVersionLimit + }; + + await logger.log(output); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new SpoSiteVersionpolicyGetCommand(); \ No newline at end of file From 071464e6cbdd946299cca137608c927a32052e9e Mon Sep 17 00:00:00 2001 From: Saurabh Tripathi Date: Thu, 9 Oct 2025 20:49:07 +0200 Subject: [PATCH 2/2] addressed code review feedback --- .devproxy/api-specs/sharepoint.yaml | 122 +++++++++--------- .../site/site-versionpolicy-get.spec.ts | 14 +- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/.devproxy/api-specs/sharepoint.yaml b/.devproxy/api-specs/sharepoint.yaml index d4f306d7653..44cd5e71979 100644 --- a/.devproxy/api-specs/sharepoint.yaml +++ b/.devproxy/api-specs/sharepoint.yaml @@ -61,10 +61,10 @@ paths: post: security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -72,10 +72,10 @@ paths: get: security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -90,15 +90,15 @@ paths: example: VersionPolicies security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl - application: - - Sites.Read.All - - Sites.ReadWrite.All - - Sites.Manage.All - - Sites.FullControl.All + - Sites.Read.All + - Sites.ReadWrite.All + - Sites.Manage.All + - Sites.FullControl.All responses: 200: description: OK @@ -106,10 +106,10 @@ paths: get: security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -125,9 +125,9 @@ paths: example: "'%2FShared%20Documents%2FMy%20Folder%20Name'" security: - delegated: - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -142,15 +142,15 @@ paths: example: "'19bbfec4-4425-4660-95cb-da1887baa7b9'" security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl - application: - - Sites.Read.All - - Sites.ReadWrite.All - - Sites.Manage.All - - Sites.FullControl.All + - Sites.Read.All + - Sites.ReadWrite.All + - Sites.Manage.All + - Sites.FullControl.All responses: 200: description: OK @@ -171,13 +171,13 @@ paths: example: 1030 security: - delegated: - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl - application: - - Sites.ReadWrite.All - - Sites.Manage.All - - Sites.FullControl.All + - Sites.ReadWrite.All + - Sites.Manage.All + - Sites.FullControl.All responses: 200: description: OK @@ -193,10 +193,10 @@ paths: example: "'%2FShared%20Documents'" security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -219,9 +219,9 @@ paths: example: "url='README.md',%20overwrite=true" security: - delegated: - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl responses: 200: description: OK @@ -229,15 +229,15 @@ paths: get: security: - delegated: - - AllSites.Read - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Read + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl - application: - - Sites.Read.All - - Sites.Manage.All - - Sites.ReadWrite.All - - Sites.FullControl.All + - Sites.Read.All + - Sites.Manage.All + - Sites.ReadWrite.All + - Sites.FullControl.All responses: 200: description: OK @@ -245,14 +245,14 @@ paths: get: security: - delegated: - - AllSites.Write - - AllSites.Manage - - AllSites.FullControl + - AllSites.Write + - AllSites.Manage + - AllSites.FullControl - application: - - Sites.Read.All - - Sites.Manage.All - - Sites.ReadWrite.All - - Sites.FullControl.All + - Sites.Read.All + - Sites.Manage.All + - Sites.ReadWrite.All + - Sites.FullControl.All responses: 200: description: OK @@ -275,7 +275,7 @@ paths: example: "overwrite=false,%20url='spfx.sppkg'" security: - delegated: - - AllSites.FullControl + - AllSites.FullControl responses: 200: description: OK @@ -298,7 +298,7 @@ paths: example: "'f55e3c17-63ea-456a-8451-48d2839760f7'" security: - delegated: - - AllSites.FullControl + - AllSites.FullControl responses: 200: description: OK diff --git a/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts b/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts index 962e73509f4..972c7d608a1 100644 --- a/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts +++ b/src/m365/spo/commands/site/site-versionpolicy-get.spec.ts @@ -73,8 +73,8 @@ describe(commands.SITE_VERSIONPOLICY_GET, () => { }); it('passes validation if valid site URL is specified', async () => { - const actual = await command.validate({ options: { siteUrl: validSiteUrl } }, commandInfo); - assert.strictEqual(actual, true); + const actual = commandOptionsSchema.safeParse({ siteUrl: validSiteUrl }); + assert.strictEqual(actual.success, true); }); it('retrieves "age" version policy settings for the specified site', async () => { @@ -92,7 +92,7 @@ describe(commands.SITE_VERSIONPOLICY_GET, () => { throw 'Invalid request'; }); - await command.action(logger, { options: { siteUrl: validSiteUrl } }); + await command.action(logger, { options: commandOptionsSchema.parse({ siteUrl: validSiteUrl }) }); assert(loggerLogSpy.calledWith({ defaultTrimMode: 'age', defaultExpireAfterDays: 200, @@ -115,7 +115,7 @@ describe(commands.SITE_VERSIONPOLICY_GET, () => { throw 'Invalid request'; }); - await command.action(logger, { options: { siteUrl: validSiteUrl } }); + await command.action(logger, { options: commandOptionsSchema.parse({ siteUrl: validSiteUrl }) }); assert(loggerLogSpy.calledWith({ defaultTrimMode: 'automatic', defaultExpireAfterDays: 30, @@ -138,7 +138,7 @@ describe(commands.SITE_VERSIONPOLICY_GET, () => { throw 'Invalid request'; }); - await command.action(logger, { options: { siteUrl: validSiteUrl, verbose: true } }); + await command.action(logger, { options: commandOptionsSchema.parse({ siteUrl: validSiteUrl, verbose: true }) }); assert(loggerLogSpy.calledWith({ defaultTrimMode: 'number', defaultExpireAfterDays: 0, @@ -157,7 +157,7 @@ describe(commands.SITE_VERSIONPOLICY_GET, () => { throw 'Invalid request'; }); - await command.action(logger, { options: { siteUrl: validSiteUrl, verbose: true } }); + await command.action(logger, { options: commandOptionsSchema.parse({ siteUrl: validSiteUrl, verbose: true }) }); assert(loggerLogSpy.calledWith({ defaultTrimMode: 'inheritTenant', defaultExpireAfterDays: null, @@ -168,7 +168,7 @@ describe(commands.SITE_VERSIONPOLICY_GET, () => { it('correctly handles API OData error', async () => { sinon.stub(request, 'get').rejects(new Error('An error has occurred')); - await assert.rejects(command.action(logger, { options: { siteUrl: validSiteUrl } }), + await assert.rejects(command.action(logger, { options: commandOptionsSchema.parse({ siteUrl: validSiteUrl }) }), new CommandError('An error has occurred')); }); }); \ No newline at end of file