|
| 1 | +import assert from 'assert'; |
| 2 | +import sinon from 'sinon'; |
| 3 | +import auth from '../../../../Auth.js'; |
| 4 | +import { cli } from '../../../../cli/cli.js'; |
| 5 | +import { CommandInfo } from '../../../../cli/CommandInfo.js'; |
| 6 | +import { Logger } from '../../../../cli/Logger.js'; |
| 7 | +import { CommandError } from '../../../../Command.js'; |
| 8 | +import request from '../../../../request.js'; |
| 9 | +import { telemetry } from '../../../../telemetry.js'; |
| 10 | +import { pid } from '../../../../utils/pid.js'; |
| 11 | +import { session } from '../../../../utils/session.js'; |
| 12 | +import { sinonUtil } from '../../../../utils/sinonUtil.js'; |
| 13 | +import commands from '../../commands.js'; |
| 14 | +import command from './tenant-site-get.js'; |
| 15 | +import { spo, TenantSiteProperties } from '../../../../utils/spo.js'; |
| 16 | +import { settingsNames } from '../../../../settingsNames.js'; |
| 17 | + |
| 18 | +describe(commands.TENANT_SITE_GET, () => { |
| 19 | + const adminUrl = 'https://contoso-admin.sharepoint.com'; |
| 20 | + const siteId = '3ae83bc5-1f27-45c1-9eee-1bd1e2ddce69'; |
| 21 | + const siteUrl = 'https://contoso.sharepoint.com/sites/Marketing'; |
| 22 | + |
| 23 | + let log: any[]; |
| 24 | + let logger: Logger; |
| 25 | + let loggerLogSpy: sinon.SinonSpy; |
| 26 | + let commandInfo: CommandInfo; |
| 27 | + |
| 28 | + const siteResponse: TenantSiteProperties = { |
| 29 | + AllowDownloadingNonWebViewableFiles: false, |
| 30 | + AllowEditing: true, |
| 31 | + AllowFileArchive: false, |
| 32 | + AllowSelfServiceUpgrade: true, |
| 33 | + AllowWebPropertyBagUpdateWhenDenyAddAndCustomizePagesIsEnabled: false, |
| 34 | + AnonymousLinkExpirationInDays: 0, |
| 35 | + ApplyToExistingDocumentLibraries: false, |
| 36 | + ApplyToNewDocumentLibraries: false, |
| 37 | + ArchivedBy: "", |
| 38 | + ArchivedFileDiskUsed: "0", |
| 39 | + ArchivedTime: "0001-01-01T00:00:00", |
| 40 | + ArchiveStatus: "NotArchived", |
| 41 | + AuthContextStrength: null, |
| 42 | + AuthenticationContextLimitedAccess: false, |
| 43 | + AuthenticationContextName: null, |
| 44 | + AverageResourceUsage: 0, |
| 45 | + BlockDownloadLinksFileType: 1, |
| 46 | + BlockDownloadMicrosoft365GroupIds: null, |
| 47 | + BlockDownloadPolicy: false, |
| 48 | + BlockDownloadPolicyFileTypeIds: null, |
| 49 | + BlockGuestsAsSiteAdmin: 0, |
| 50 | + BonusDiskQuota: "920", |
| 51 | + ClearGroupId: false, |
| 52 | + ClearRestrictedAccessControl: false, |
| 53 | + CommentsOnSitePagesDisabled: false, |
| 54 | + CompatibilityLevel: 15, |
| 55 | + ConditionalAccessPolicy: 0, |
| 56 | + CreatedTime: "2021-10-12T09:54:16.52", |
| 57 | + CurrentResourceUsage: 0, |
| 58 | + DefaultLinkPermission: 0, |
| 59 | + DefaultLinkToExistingAccess: false, |
| 60 | + DefaultLinkToExistingAccessReset: false, |
| 61 | + DefaultShareLinkRole: 0, |
| 62 | + DefaultShareLinkScope: -1, |
| 63 | + DefaultSharingLinkType: 0, |
| 64 | + DenyAddAndCustomizePages: 2, |
| 65 | + Description: "", |
| 66 | + DisableAppViews: 2, |
| 67 | + DisableCompanyWideSharingLinks: 2, |
| 68 | + DisableFlows: 2, |
| 69 | + DisableSiteBranding: false, |
| 70 | + EnableAutoExpirationVersionTrim: false, |
| 71 | + ExcludeBlockDownloadPolicySiteOwners: false, |
| 72 | + ExcludeBlockDownloadSharePointGroups: [], |
| 73 | + ExcludedBlockDownloadGroupIds: [], |
| 74 | + ExpireVersionsAfterDays: 0, |
| 75 | + ExternalUserExpirationInDays: 0, |
| 76 | + FileTypesForVersionExpiration: null, |
| 77 | + GroupId: "00000000-0000-0000-0000-000000000000", |
| 78 | + GroupOwnerLoginName: "c:0o.c|federateddirectoryclaimprovider|00000000-0000-0000-0000-000000000000_o", |
| 79 | + HasHolds: false, |
| 80 | + HidePeoplePreviewingFiles: false, |
| 81 | + HidePeopleWhoHaveListsOpen: false, |
| 82 | + HubSiteId: "af80c11f-0138-4d72-bb37-514542c3aabb", |
| 83 | + IBMode: "", |
| 84 | + IBSegments: [], |
| 85 | + IBSegmentsToAdd: null, |
| 86 | + IBSegmentsToRemove: null, |
| 87 | + InheritVersionPolicyFromTenant: true, |
| 88 | + IsAuthoritative: false, |
| 89 | + IsGroupOwnerSiteAdmin: false, |
| 90 | + IsHubSite: false, |
| 91 | + IsTeamsChannelConnected: false, |
| 92 | + IsTeamsConnected: false, |
| 93 | + LastContentModifiedDate: "2025-10-03T00:20:28.62", |
| 94 | + Lcid: "1033", |
| 95 | + LimitedAccessFileType: 1, |
| 96 | + ListsShowHeaderAndNavigation: false, |
| 97 | + LockIssue: null, |
| 98 | + LockReason: 0, |
| 99 | + LockState: "Unlock", |
| 100 | + LoopDefaultSharingLinkRole: 0, |
| 101 | + LoopDefaultSharingLinkScope: -1, |
| 102 | + MajorVersionLimit: 0, |
| 103 | + MajorWithMinorVersionsLimit: 0, |
| 104 | + MediaTranscription: 0, |
| 105 | + OverrideBlockUserInfoVisibility: 0, |
| 106 | + OverrideSharingCapability: false, |
| 107 | + OverrideTenantAnonymousLinkExpirationPolicy: false, |
| 108 | + OverrideTenantExternalUserExpirationPolicy: false, |
| 109 | + |
| 110 | + |
| 111 | + OwnerLoginName: "i:0#.f|membership|[email protected]", |
| 112 | + OwnerName: "john", |
| 113 | + PWAEnabled: 1, |
| 114 | + ReadOnlyAccessPolicy: false, |
| 115 | + ReadOnlyForBlockDownloadPolicy: false, |
| 116 | + ReadOnlyForUnmanagedDevices: false, |
| 117 | + RelatedGroupId: "00000000-0000-0000-0000-000000000000", |
| 118 | + RemoveVersionExpirationFileTypeOverride: null, |
| 119 | + RequestFilesLinkEnabled: false, |
| 120 | + RequestFilesLinkExpirationInDays: -1, |
| 121 | + RestrictContentOrgWideSearch: false, |
| 122 | + RestrictedAccessControl: false, |
| 123 | + RestrictedAccessControlGroups: [], |
| 124 | + RestrictedAccessControlGroupsToAdd: null, |
| 125 | + RestrictedAccessControlGroupsToRemove: null, |
| 126 | + RestrictedContentDiscoveryforCopilotAndAgents: false, |
| 127 | + RestrictedToRegion: 3, |
| 128 | + SandboxedCodeActivationCapability: 2, |
| 129 | + SensitivityLabel: "00000000-0000-0000-0000-000000000000", |
| 130 | + SensitivityLabel2: null, |
| 131 | + SetOwnerWithoutUpdatingSecondaryAdmin: false, |
| 132 | + SharingAllowedDomainList: "", |
| 133 | + SharingBlockedDomainList: "", |
| 134 | + SharingCapability: 0, |
| 135 | + SharingDomainRestrictionMode: 0, |
| 136 | + SharingLockDownCanBeCleared: true, |
| 137 | + SharingLockDownEnabled: false, |
| 138 | + ShowPeoplePickerSuggestionsForGuestUsers: false, |
| 139 | + SiteDefinedSharingCapability: 0, |
| 140 | + SiteId: "8f6fdeda-f6ff-4d39-8a8c-fe86565afefd", |
| 141 | + SocialBarOnSitePagesDisabled: false, |
| 142 | + Status: "Active", |
| 143 | + StorageMaximumLevel: "26214400", |
| 144 | + StorageQuotaType: null, |
| 145 | + StorageUsage: "3", |
| 146 | + StorageWarningLevel: "25574400", |
| 147 | + TeamsChannelType: 0, |
| 148 | + Template: "SITEPAGEPUBLISHING#0", |
| 149 | + TimeZoneId: 2, |
| 150 | + Title: "Marketing and Communications", |
| 151 | + TitleTranslations: null, |
| 152 | + Url: "https://contoso.sharepoint.com/sites/marketing", |
| 153 | + UserCodeMaximumLevel: 300, |
| 154 | + UserCodeWarningLevel: 200, |
| 155 | + VersionCount: "7", |
| 156 | + VersionPolicyFileTypeOverride: [], |
| 157 | + VersionSize: "0", |
| 158 | + WebsCount: 1 |
| 159 | + }; |
| 160 | + |
| 161 | + before(() => { |
| 162 | + sinon.stub(auth, 'restoreAuth').resolves(); |
| 163 | + sinon.stub(telemetry, 'trackEvent').resolves(); |
| 164 | + sinon.stub(pid, 'getProcessName').returns(''); |
| 165 | + sinon.stub(session, 'getId').returns(''); |
| 166 | + auth.connection.active = true; |
| 167 | + commandInfo = cli.getCommandInfo(command); |
| 168 | + sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName: string, defaultValue: any) => { |
| 169 | + if (settingName === settingsNames.prompt) { |
| 170 | + return false; |
| 171 | + } |
| 172 | + |
| 173 | + return defaultValue; |
| 174 | + }); |
| 175 | + }); |
| 176 | + |
| 177 | + beforeEach(() => { |
| 178 | + log = []; |
| 179 | + logger = { |
| 180 | + log: async (msg: string) => { |
| 181 | + log.push(msg); |
| 182 | + }, |
| 183 | + logRaw: async (msg: string) => { |
| 184 | + log.push(msg); |
| 185 | + }, |
| 186 | + logToStderr: async (msg: string) => { |
| 187 | + log.push(msg); |
| 188 | + } |
| 189 | + }; |
| 190 | + loggerLogSpy = sinon.spy(logger, 'log'); |
| 191 | + sinon.stub(spo, 'getSpoAdminUrl').resolves(adminUrl); |
| 192 | + }); |
| 193 | + |
| 194 | + afterEach(() => { |
| 195 | + sinonUtil.restore([ |
| 196 | + request.get, |
| 197 | + request.post, |
| 198 | + cli.handleMultipleResultsFound, |
| 199 | + spo.getSpoAdminUrl, |
| 200 | + spo.getSiteAdminPropertiesByUrl |
| 201 | + ]); |
| 202 | + }); |
| 203 | + |
| 204 | + after(() => { |
| 205 | + sinon.restore(); |
| 206 | + auth.connection.active = false; |
| 207 | + }); |
| 208 | + |
| 209 | + it('has correct name', () => { |
| 210 | + assert.strictEqual(command.name, commands.TENANT_SITE_GET); |
| 211 | + }); |
| 212 | + |
| 213 | + it('has a description', () => { |
| 214 | + assert.notStrictEqual(command.description, null); |
| 215 | + }); |
| 216 | + |
| 217 | + it('retrieves site by id', async () => { |
| 218 | + sinon.stub(request, 'get').callsFake(async (opts) => { |
| 219 | + if (opts.url === `${adminUrl}/_api/SPO.Tenant/sites('${siteId}')?$select=Url`) { |
| 220 | + return { Url: siteUrl }; |
| 221 | + } |
| 222 | + throw 'Invalid request'; |
| 223 | + }); |
| 224 | + |
| 225 | + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves(siteResponse); |
| 226 | + |
| 227 | + await command.action(logger, { options: { id: siteId, verbose: true } }); |
| 228 | + assert(loggerLogSpy.calledWith(siteResponse)); |
| 229 | + }); |
| 230 | + |
| 231 | + it('retrieves site by url', async () => { |
| 232 | + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves(siteResponse); |
| 233 | + |
| 234 | + await command.action(logger, { options: { url: siteUrl, verbose: true } }); |
| 235 | + assert(loggerLogSpy.calledWith(siteResponse)); |
| 236 | + }); |
| 237 | + |
| 238 | + it('retrieves site by title (single result)', async () => { |
| 239 | + sinon.stub(request, 'post').callsFake(async (opts) => { |
| 240 | + if (opts.url === `${adminUrl}/_api/web/lists/GetByTitle('DO_NOT_DELETE_SPLIST_TENANTADMIN_AGGREGATED_SITECOLLECTIONS')/RenderListDataAsStream`) { |
| 241 | + return { Row: [{ Title: 'Marketing', SiteUrl: siteUrl, SiteId: `/Guid(${siteId})/` }] }; |
| 242 | + } |
| 243 | + throw 'Invalid request'; |
| 244 | + }); |
| 245 | + |
| 246 | + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves(siteResponse); |
| 247 | + |
| 248 | + await command.action(logger, { options: { title: 'Marketing', verbose: true } }); |
| 249 | + assert(loggerLogSpy.calledWith(siteResponse)); |
| 250 | + }); |
| 251 | + |
| 252 | + it('retrieves site by title (multiple results prompts)', async () => { |
| 253 | + sinon.stub(request, 'post').callsFake(async (opts) => { |
| 254 | + if (opts.url === `${adminUrl}/_api/web/lists/GetByTitle('DO_NOT_DELETE_SPLIST_TENANTADMIN_AGGREGATED_SITECOLLECTIONS')/RenderListDataAsStream`) { |
| 255 | + return { |
| 256 | + Row: [ |
| 257 | + { Title: 'Marketing', SiteUrl: siteUrl, SiteId: `/Guid(${siteId})/` }, |
| 258 | + { Title: 'Marketing', SiteUrl: 'https://contoso.sharepoint.com/sites/Marketing2', SiteId: '/Guid(53dec431-9d4f-415b-b12b-010259d5b4e1)/' } |
| 259 | + ] |
| 260 | + }; |
| 261 | + } |
| 262 | + throw 'Invalid request'; |
| 263 | + }); |
| 264 | + |
| 265 | + sinon.stub(cli, 'handleMultipleResultsFound').resolves({ url: siteUrl } as any); |
| 266 | + |
| 267 | + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves(siteResponse); |
| 268 | + |
| 269 | + await command.action(logger, { options: { title: 'Marketing', verbose: true } }); |
| 270 | + assert(loggerLogSpy.calledWith(siteResponse)); |
| 271 | + }); |
| 272 | + |
| 273 | + it('handles error when specified site by title not found', async () => { |
| 274 | + sinon.stub(request, 'post').callsFake(async (opts) => { |
| 275 | + if (opts.url === `${adminUrl}/_api/web/lists/GetByTitle('DO_NOT_DELETE_SPLIST_TENANTADMIN_AGGREGATED_SITECOLLECTIONS')/RenderListDataAsStream`) { |
| 276 | + return { Row: [] }; |
| 277 | + } |
| 278 | + throw 'Invalid request'; |
| 279 | + }); |
| 280 | + |
| 281 | + await assert.rejects(command.action(logger, { options: { title: 'Marketing', verbose: true } } as any), new CommandError("The specified site 'Marketing' does not exist.")); |
| 282 | + }); |
| 283 | + |
| 284 | + it('fails validation when specifying none of id, title, url', async () => { |
| 285 | + const commandOptionsSchema = commandInfo.command.getSchemaToParse()!; |
| 286 | + const refined = commandInfo.command.getRefinedSchema!(commandOptionsSchema as any)!; |
| 287 | + const actual = refined.safeParse({}); |
| 288 | + assert.strictEqual(actual.success, false); |
| 289 | + }); |
| 290 | + |
| 291 | + it('fails validation when specifying multiple of id, title, url', async () => { |
| 292 | + const commandOptionsSchema = commandInfo.command.getSchemaToParse()!; |
| 293 | + const refined = commandInfo.command.getRefinedSchema!(commandOptionsSchema as any)!; |
| 294 | + const actual = refined.safeParse({ id: siteId, url: siteUrl }); |
| 295 | + assert.strictEqual(actual.success, false); |
| 296 | + }); |
| 297 | + |
| 298 | + it('handles OData error when site not found', async () => { |
| 299 | + sinon.stub(request, 'get').callsFake(async (opts) => { |
| 300 | + if (opts.url === `${adminUrl}/_api/SPO.Tenant/sites('${siteId}')?$select=Url`) { |
| 301 | + const error = { |
| 302 | + error: { |
| 303 | + 'odata.error': { |
| 304 | + code: '-2147024891, System.UnauthorizedAccessException', |
| 305 | + message: { |
| 306 | + lang: 'en-US', |
| 307 | + value: 'Attempted to perform an unauthorized operation.' |
| 308 | + } |
| 309 | + } |
| 310 | + } |
| 311 | + }; |
| 312 | + throw error; |
| 313 | + } |
| 314 | + throw 'Invalid request'; |
| 315 | + }); |
| 316 | + |
| 317 | + await assert.rejects(command.action(logger, { options: { id: siteId, verbose: true } } as any), new CommandError('Attempted to perform an unauthorized operation.')); |
| 318 | + }); |
| 319 | +}); |
| 320 | + |
| 321 | + |
0 commit comments