diff --git a/plugins/techdocs-node/__mocks__/@azure/identity.ts b/plugins/techdocs-node/__mocks__/@azure/identity.ts deleted file mode 100644 index 6aeb738963..0000000000 --- a/plugins/techdocs-node/__mocks__/@azure/identity.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * 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 - * - * http://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. - */ -export class DefaultAzureCredential { - /** - * Creates an instance of the DefaultAzureCredential class. - */ -} diff --git a/plugins/techdocs-node/__mocks__/@azure/storage-blob.ts b/plugins/techdocs-node/__mocks__/@azure/storage-blob.ts deleted file mode 100644 index 1d63e7266a..0000000000 --- a/plugins/techdocs-node/__mocks__/@azure/storage-blob.ts +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * 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 - * - * http://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. - */ -import { - BlobUploadCommonResponse, - ContainerGetPropertiesResponse, -} from '@azure/storage-blob'; -import { EventEmitter } from 'events'; -import { IStorageFilesMock } from '../../src/testUtils/types'; - -const storage = global.storageFilesMock as IStorageFilesMock; - -export class BlockBlobClient { - private readonly blobName; - - constructor(blobName: string) { - this.blobName = blobName; - } - - uploadFile(source: string): Promise { - storage.writeFile(this.blobName, source); - return Promise.resolve({ - _response: { - request: { - url: `https://example.blob.core.windows.net`, - } as any, - status: 200, - headers: {} as any, - }, - }); - } - - exists() { - return storage.fileExists(this.blobName); - } - - download() { - const emitter = new EventEmitter(); - setTimeout(() => { - if (storage.fileExists(this.blobName)) { - emitter.emit('data', storage.readFile(this.blobName)); - emitter.emit('end'); - } else { - emitter.emit( - 'error', - new Error(`The file ${this.blobName} does not exist!`), - ); - } - }, 0); - return Promise.resolve({ - readableStreamBody: emitter, - }); - } -} - -class BlockBlobClientFailUpload extends BlockBlobClient { - uploadFile(): Promise { - return Promise.resolve({ - _response: { - request: { - url: `https://example.blob.core.windows.net`, - } as any, - status: 500, - headers: {} as any, - }, - }); - } -} - -class ContainerClientIterator { - private containerName: string; - - constructor(containerName) { - this.containerName = containerName; - } - - async next() { - if ( - this.containerName === 'delete_stale_files_success' || - this.containerName === 'delete_stale_files_error' - ) { - return { - value: { - segment: { - blobItems: [{ name: `stale_file.png` }], - }, - }, - }; - } - return { - value: { - segment: { - blobItems: [], - }, - }, - }; - } -} - -export class ContainerClient { - getProperties(): Promise { - return Promise.resolve({ - _response: { - request: { - url: `https://example.blob.core.windows.net`, - } as any, - status: 200, - headers: {} as any, - parsedHeaders: {}, - }, - }); - } - - getBlockBlobClient(blobName: string) { - return new BlockBlobClient(blobName); - } - - listBlobsFlat() { - return { - byPage: () => { - return new ContainerClientIterator(this.containerName); - }, - }; - } - - deleteBlob() { - if (this.containerName === 'delete_stale_files_error') { - throw new Error('Message'); - } - } -} - -class ContainerClientFailGetProperties extends ContainerClient { - getProperties(): Promise { - return Promise.resolve({ - _response: { - request: { - url: `https://example.blob.core.windows.net`, - } as any, - status: 404, - headers: {} as any, - parsedHeaders: {}, - }, - }); - } -} - -class ContainerClientFailUpload extends ContainerClient { - getBlockBlobClient(blobName: string) { - return new BlockBlobClientFailUpload(blobName); - } -} - -export class BlobServiceClient { - private readonly url; - private readonly credential; - - constructor(url: string, credential?: StorageSharedKeyCredential) { - storage.emptyFiles(); - this.url = url; - this.credential = credential; - } - - getContainerClient(containerName: string) { - if (containerName === 'bad_container') { - return new ContainerClientFailGetProperties(); - } - if (this.credential.accountName === 'bad_account_credentials') { - return new ContainerClientFailUpload(); - } - return new ContainerClient(); - } -} - -export class StorageSharedKeyCredential { - private readonly accountName; - private readonly accountKey; - - constructor(accountName: string, accountKey: string) { - this.accountName = accountName; - this.accountKey = accountKey; - } -} diff --git a/plugins/techdocs-node/src/stages/publish/azureBlobStorage.test.ts b/plugins/techdocs-node/src/stages/publish/azureBlobStorage.test.ts index 7af5338d88..6d5848b0d8 100644 --- a/plugins/techdocs-node/src/stages/publish/azureBlobStorage.test.ts +++ b/plugins/techdocs-node/src/stages/publish/azureBlobStorage.test.ts @@ -23,10 +23,207 @@ import mockFs from 'mock-fs'; import path from 'path'; import fs from 'fs-extra'; import { AzureBlobStoragePublish } from './azureBlobStorage'; +import { EventEmitter } from 'events'; +import { + BlobUploadCommonResponse, + ContainerGetPropertiesResponse, +} from '@azure/storage-blob'; +import { + storageRootDir, + StorageFilesMock, +} from '../../testUtils/StorageFilesMock'; -// NOTE: /plugins/techdocs-node/__mocks__ is being used to mock Azure client library +jest.mock('@azure/identity', () => ({ + __esModule: true, + DefaultAzureCredential: class {}, +})); -const rootDir = (global as any).rootDir; // Set by setupTests.ts +jest.mock('@azure/storage-blob', () => { + class BlockBlobClient { + constructor( + private readonly blobName: string, + private readonly storage: StorageFilesMock, + ) {} + + uploadFile(source: string): Promise { + this.storage.writeFile(this.blobName, source); + return Promise.resolve({ + _response: { + request: { + url: `https://example.blob.core.windows.net`, + } as any, + status: 200, + headers: {} as any, + }, + }); + } + + exists() { + return this.storage.fileExists(this.blobName); + } + + download() { + const emitter = new EventEmitter(); + setTimeout(() => { + if (this.storage.fileExists(this.blobName)) { + emitter.emit('data', this.storage.readFile(this.blobName)); + emitter.emit('end'); + } else { + emitter.emit( + 'error', + new Error(`The file ${this.blobName} does not exist!`), + ); + } + }, 0); + return Promise.resolve({ + readableStreamBody: emitter, + }); + } + } + + class BlockBlobClientFailUpload extends BlockBlobClient { + uploadFile(): Promise { + return Promise.resolve({ + _response: { + request: { + url: `https://example.blob.core.windows.net`, + } as any, + status: 500, + headers: {} as any, + }, + }); + } + } + + class ContainerClientIterator { + private containerName: string; + + constructor(containerName: string) { + this.containerName = containerName; + } + + async next() { + if ( + this.containerName === 'delete_stale_files_success' || + this.containerName === 'delete_stale_files_error' + ) { + return { + value: { + segment: { + blobItems: [{ name: `stale_file.png` }], + }, + }, + }; + } + return { + value: { + segment: { + blobItems: [], + }, + }, + }; + } + } + + class ContainerClient { + constructor( + private readonly containerName: string, + protected readonly storage: StorageFilesMock, + ) {} + + getProperties(): Promise { + return Promise.resolve({ + _response: { + request: { + url: `https://example.blob.core.windows.net`, + } as any, + status: 200, + headers: {} as any, + parsedHeaders: {}, + }, + }); + } + + getBlockBlobClient(blobName: string) { + return new BlockBlobClient(blobName, this.storage); + } + + listBlobsFlat() { + return { + byPage: () => { + return new ContainerClientIterator(this.containerName); + }, + }; + } + + deleteBlob() { + if (this.containerName === 'delete_stale_files_error') { + throw new Error('Message'); + } + } + } + + class ContainerClientFailGetProperties extends ContainerClient { + getProperties(): Promise { + return Promise.resolve({ + _response: { + request: { + url: `https://example.blob.core.windows.net`, + } as any, + status: 404, + headers: {} as any, + parsedHeaders: {}, + }, + }); + } + } + + class ContainerClientFailUpload extends ContainerClient { + getBlockBlobClient(blobName: string) { + return new BlockBlobClientFailUpload(blobName, this.storage); + } + } + + class BlobServiceClient { + storage = new StorageFilesMock(); + + constructor( + public readonly url: string, + private readonly credential?: StorageSharedKeyCredential, + ) { + this.storage.emptyFiles(); + } + + getContainerClient(containerName: string) { + if (containerName === 'bad_container') { + return new ContainerClientFailGetProperties( + containerName, + this.storage, + ); + } + if (this.credential?.accountName === 'bad_account_credentials') { + return new ContainerClientFailUpload(containerName, this.storage); + } + return new ContainerClient(containerName, this.storage); + } + } + + class StorageSharedKeyCredential { + readonly accountName; + readonly accountKey; + + constructor(accountName: string, accountKey: string) { + this.accountName = accountName; + this.accountKey = accountKey; + } + } + + return { + __esModule: true, + BlobServiceClient, + StorageSharedKeyCredential, + }; +}); const getEntityRootDir = (entity: Entity) => { const { @@ -34,7 +231,7 @@ const getEntityRootDir = (entity: Entity) => { metadata: { namespace, name }, } = entity; - return path.join(rootDir, namespace || DEFAULT_NAMESPACE, kind, name); + return path.join(storageRootDir, namespace || DEFAULT_NAMESPACE, kind, name); }; const logger = getVoidLogger(); @@ -178,7 +375,7 @@ describe('AzureBlobStoragePublish', () => { it('should fail to publish a directory', async () => { const wrongPathToGeneratedDirectory = path.join( - rootDir, + storageRootDir, 'wrong', 'path', 'to', diff --git a/plugins/techdocs-node/src/stages/publish/publish.test.ts b/plugins/techdocs-node/src/stages/publish/publish.test.ts index 4537313edf..ab300412a4 100644 --- a/plugins/techdocs-node/src/stages/publish/publish.test.ts +++ b/plugins/techdocs-node/src/stages/publish/publish.test.ts @@ -31,6 +31,11 @@ const discovery: jest.Mocked = { getExternalBaseUrl: jest.fn(), }; +jest.mock('@azure/identity', () => ({ + __esModule: true, + DefaultAzureCredential: class {}, +})); + describe('Publisher', () => { beforeEach(() => { jest.resetModules(); // clear the cache