From b27aa7c7acc066afd6a6f36a371f40a0d9fdee01 Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Thu, 18 Feb 2021 02:31:21 -0300 Subject: [PATCH 1/9] Add tests for fetchTechDocsMetadata Signed-off-by: vitorgrenzel --- .../__mocks__/@azure/storage-blob.ts | 19 ++++++ .../stages/publish/azureBlobStorage.test.ts | 67 ++++++++++++++++++- .../src/stages/publish/azureBlobStorage.ts | 34 +++++++--- 3 files changed, 107 insertions(+), 13 deletions(-) diff --git a/packages/techdocs-common/__mocks__/@azure/storage-blob.ts b/packages/techdocs-common/__mocks__/@azure/storage-blob.ts index 4fc1e4c374..8734b58891 100644 --- a/packages/techdocs-common/__mocks__/@azure/storage-blob.ts +++ b/packages/techdocs-common/__mocks__/@azure/storage-blob.ts @@ -17,6 +17,7 @@ import type { BlobUploadCommonResponse, ContainerGetPropertiesResponse, } from '@azure/storage-blob'; +import { EventEmitter } from 'events'; import fs from 'fs-extra'; import os from 'os'; import path from 'path'; @@ -62,6 +63,24 @@ export class BlockBlobClient { exists() { return checkFileExists(this.blobName); } + + download() { + const emitter = new EventEmitter(); + process.nextTick(() => { + if (fs.existsSync(this.blobName)) { + emitter.emit('data', Buffer.from(fs.readFileSync(this.blobName))); + } else { + emitter.emit( + 'error', + new Error(`The file ${this.blobName} doest not exist!`), + ); + } + emitter.emit('end'); + }); + return Promise.resolve({ + readableStreamBody: emitter, + }); + } } class BlockBlobClientFailUpload extends BlockBlobClient { diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts index cf1ea60592..e1edaa61bb 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts @@ -20,8 +20,9 @@ import mockFs from 'mock-fs'; import os from 'os'; import path from 'path'; import { AzureBlobStoragePublish } from './azureBlobStorage'; -import { PublisherBase } from './types'; - +import { PublisherBase, TechDocsMetadata } from './types'; +import type { Entity, EntityName } from '@backstage/catalog-model'; +import type { Logger } from 'winston'; // NOTE: /packages/techdocs-common/__mocks__ is being used to mock Azure client library const createMockEntity = (annotations = {}) => { @@ -38,6 +39,12 @@ const createMockEntity = (annotations = {}) => { }; }; +const createMockEntityName = (): EntityName => ({ + kind: 'TestKind', + name: 'test-component-name', + namespace: 'test-namespace', +}); + const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; const getEntityRootDir = (entity: Entity) => { const { @@ -255,4 +262,60 @@ describe('error reporting', () => { mockFs.restore(); }); + + describe('fetchTechDocsMetadata', () => { + it('should return tech docs metadata', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': + '{"site_name": "backstage", "site_description": "site_content"}', + }, + }); + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return tech docs metadata when json encoded with single quotes', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': `{'site_name': 'backstage', 'site_description': 'site_content'}`, + }, + }); + + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return an error if the techdocs_metadata.json file is not present', async () => { + const entityNameMock = createMockEntityName(); + + await publisher + .fetchTechDocsMetadata(entityNameMock) + .catch(error => + expect(error.message).toEqual( + expect.stringContaining('TechDocs metadata fetch'), + ), + ); + }); + }); }); diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts index 2136ee2bc2..f7d2afc117 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts @@ -201,13 +201,13 @@ export class AzureBlobStoragePublish implements PublisherBase { return; } body + .on('data', chunk => { + fileStreamChunks.push(chunk); + }) .on('error', e => { this.logger.error(e.message); reject(e.message); }) - .on('data', chunk => { - fileStreamChunks.push(chunk); - }) .on('end', () => { resolve(Buffer.concat(fileStreamChunks)); }); @@ -220,16 +220,28 @@ export class AzureBlobStoragePublish implements PublisherBase { ): Promise { const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`; try { - return await new Promise(resolve => { - const download = this.download( - this.containerName, - `${entityRootDir}/techdocs_metadata.json`, - ); - resolve(JSON5.parse(download.toString())); + return await new Promise(async (resolve, reject) => { + try { + const techdocsMetadataJson = await this.download( + this.containerName, + `${entityRootDir}/techdocs_metadata.json`, + ); + if (!techdocsMetadataJson) { + throw new Error( + `Unable to parse the techdocs metadata file ${entityRootDir}/techdocs_metadata.json.`, + ); + } + const techdocsMetadata = JSON5.parse( + techdocsMetadataJson.toString('utf-8'), + ); + resolve(techdocsMetadata); + } catch (err) { + this.logger.error(err.message); + reject(new Error(err.message)); + } }); } catch (e) { - this.logger.error(e.message); - throw e; + throw new Error(`TechDocs metadata fetch failed, ${e.message}`); } } From 5773488da20d136ff37df03f8fd6f74145c43e62 Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Thu, 18 Feb 2021 02:34:24 -0300 Subject: [PATCH 2/9] Add changeset Signed-off-by: vitorgrenzel --- .changeset/techdocs-improve-tests.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/techdocs-improve-tests.md diff --git a/.changeset/techdocs-improve-tests.md b/.changeset/techdocs-improve-tests.md new file mode 100644 index 0000000000..4c8e68482f --- /dev/null +++ b/.changeset/techdocs-improve-tests.md @@ -0,0 +1,5 @@ +--- +'@backstage/techdocs-common': patch +--- + +Improved tests fetchTechDocsMetadata to AzureBlobStorage. From 1ad2b0ed92c1073222f4eba0610033c8bb9effb1 Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Thu, 18 Feb 2021 02:36:42 -0300 Subject: [PATCH 3/9] Add tests for fetchTechDocsMetadata Signed-off-by: vitorgrenzel --- .../techdocs-common/src/stages/publish/azureBlobStorage.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts index f7d2afc117..1d172bf6c9 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts @@ -201,13 +201,13 @@ export class AzureBlobStoragePublish implements PublisherBase { return; } body - .on('data', chunk => { - fileStreamChunks.push(chunk); - }) .on('error', e => { this.logger.error(e.message); reject(e.message); }) + .on('data', chunk => { + fileStreamChunks.push(chunk); + }) .on('end', () => { resolve(Buffer.concat(fileStreamChunks)); }); From 71684c6cb4f3732d7198e1453b207bf2e6cb4f93 Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Mon, 22 Feb 2021 10:31:34 -0300 Subject: [PATCH 4/9] Add tests for fetchTechDocsMetadata Signed-off-by: vitorgrenzel --- .../__mocks__/@azure/storage-blob.ts | 32 +++++--- .../stages/publish/azureBlobStorage.test.ts | 73 ++++++++++++++++++- .../src/stages/publish/azureBlobStorage.ts | 5 +- 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/packages/techdocs-common/__mocks__/@azure/storage-blob.ts b/packages/techdocs-common/__mocks__/@azure/storage-blob.ts index 8734b58891..11a034db09 100644 --- a/packages/techdocs-common/__mocks__/@azure/storage-blob.ts +++ b/packages/techdocs-common/__mocks__/@azure/storage-blob.ts @@ -23,6 +23,7 @@ import os from 'os'; import path from 'path'; const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + /** * @param sourceFile Relative path to entity root dir. Contains either / or \ as file separator * depending upon the OS. @@ -49,14 +50,20 @@ export class BlockBlobClient { } uploadFile(source: string): Promise { - return Promise.resolve({ - _response: { - request: { - url: `https://example.blob.core.windows.net`, - } as any, - status: 200, - headers: {} as any, - }, + return new Promise((resolve, reject) => { + if (!fs.existsSync(source)) { + reject(`The file ${source} does not exist`); + } else { + resolve({ + _response: { + request: { + url: `https://example.blob.core.windows.net`, + } as any, + status: 200, + headers: {} as any, + }, + }); + } }); } @@ -65,17 +72,18 @@ export class BlockBlobClient { } download() { + const filePath = path.join(rootDir, this.blobName); const emitter = new EventEmitter(); process.nextTick(() => { - if (fs.existsSync(this.blobName)) { - emitter.emit('data', Buffer.from(fs.readFileSync(this.blobName))); + if (fs.existsSync(filePath)) { + emitter.emit('data', Buffer.from(fs.readFileSync(filePath))); + emitter.emit('end'); } else { emitter.emit( 'error', - new Error(`The file ${this.blobName} doest not exist!`), + new Error(`The file ${filePath} does not exist !`), ); } - emitter.emit('end'); }); return Promise.resolve({ readableStreamBody: emitter, diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts index e1edaa61bb..dc75a4648c 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts @@ -14,15 +14,18 @@ * limitations under the License. */ import { getVoidLogger } from '@backstage/backend-common'; -import { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model'; +import { + Entity, + EntityName, + ENTITY_DEFAULT_NAMESPACE, +} from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; import mockFs from 'mock-fs'; import os from 'os'; import path from 'path'; import { AzureBlobStoragePublish } from './azureBlobStorage'; import { PublisherBase, TechDocsMetadata } from './types'; -import type { Entity, EntityName } from '@backstage/catalog-model'; -import type { Logger } from 'winston'; + // NOTE: /packages/techdocs-common/__mocks__ is being used to mock Azure client library const createMockEntity = (annotations = {}) => { @@ -46,6 +49,7 @@ const createMockEntityName = (): EntityName => ({ }); const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + const getEntityRootDir = (entity: Entity) => { const { kind, @@ -170,6 +174,69 @@ describe('publishing with valid credentials', () => { expect(await publisher.hasDocsBeenGenerated(entity)).toBe(false); }); }); + + describe('fetchTechDocsMetadata', () => { + it('should return tech docs metadata', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': + '{"site_name": "backstage", "site_description": "site_content"}', + }, + }); + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return tech docs metadata when json encoded with single quotes', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': `{'site_name': 'backstage', 'site_description': 'site_content'}`, + }, + }); + + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return an error if the techdocs_metadata.json file is not present', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + await publisher + .fetchTechDocsMetadata(entityNameMock) + .catch(error => + expect(error).toEqual( + new Error( + `TechDocs metadata fetch failed, The file ${path.join( + entityRootDir, + 'techdocs_metadata.json', + )} does not exist !`, + ), + ), + ); + }); + }); }); describe('error reporting', () => { diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts index 1d172bf6c9..a0f512df6e 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts @@ -201,10 +201,7 @@ export class AzureBlobStoragePublish implements PublisherBase { return; } body - .on('error', e => { - this.logger.error(e.message); - reject(e.message); - }) + .on('error', reject) .on('data', chunk => { fileStreamChunks.push(chunk); }) From abfa1606238ffac05598c27dfc7701287d5a58d1 Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Mon, 22 Feb 2021 14:48:28 -0300 Subject: [PATCH 5/9] Update mock funcion uploadFile Signed-off-by: vitorgrenzel --- .../__mocks__/@azure/storage-blob.ts | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/techdocs-common/__mocks__/@azure/storage-blob.ts b/packages/techdocs-common/__mocks__/@azure/storage-blob.ts index 11a034db09..0d480a7fa9 100644 --- a/packages/techdocs-common/__mocks__/@azure/storage-blob.ts +++ b/packages/techdocs-common/__mocks__/@azure/storage-blob.ts @@ -50,20 +50,17 @@ export class BlockBlobClient { } uploadFile(source: string): Promise { - return new Promise((resolve, reject) => { - if (!fs.existsSync(source)) { - reject(`The file ${source} does not exist`); - } else { - resolve({ - _response: { - request: { - url: `https://example.blob.core.windows.net`, - } as any, - status: 200, - headers: {} as any, - }, - }); - } + if (!fs.existsSync(source)) { + return Promise.reject(new Error(`The file ${source} does not exist`)); + } + return Promise.resolve({ + _response: { + request: { + url: `https://example.blob.core.windows.net`, + } as any, + status: 200, + headers: {} as any, + }, }); } From a21442b1cd4533458f0fd30bc61184dfc4eef6fa Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Mon, 22 Feb 2021 14:56:02 -0300 Subject: [PATCH 6/9] Update test techdocs_metadata.json file is not present Signed-off-by: vitorgrenzel --- .../stages/publish/azureBlobStorage.test.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts index dc75a4648c..dd94cdafde 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts @@ -223,18 +223,21 @@ describe('publishing with valid credentials', () => { const entity = createMockEntity(); const entityRootDir = getEntityRootDir(entity); - await publisher - .fetchTechDocsMetadata(entityNameMock) - .catch(error => - expect(error).toEqual( - new Error( - `TechDocs metadata fetch failed, The file ${path.join( - entityRootDir, - 'techdocs_metadata.json', - )} does not exist !`, - ), - ), - ); + let error; + try { + await publisher.fetchTechDocsMetadata(entityNameMock); + } catch (e) { + error = e; + } + + expect(error).toEqual( + new Error( + `TechDocs metadata fetch failed, The file ${path.join( + entityRootDir, + 'techdocs_metadata.json', + )} does not exist !`, + ), + ); }); }); }); From 52396fbe9dd8095a312e115d186452aed6c7f44f Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Mon, 22 Feb 2021 15:14:34 -0300 Subject: [PATCH 7/9] Update test techdocs_metadata.json file is not present Signed-off-by: vitorgrenzel --- .../src/stages/publish/azureBlobStorage.test.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts index dd94cdafde..44262768a5 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts @@ -379,13 +379,16 @@ describe('error reporting', () => { it('should return an error if the techdocs_metadata.json file is not present', async () => { const entityNameMock = createMockEntityName(); - await publisher - .fetchTechDocsMetadata(entityNameMock) - .catch(error => - expect(error.message).toEqual( - expect.stringContaining('TechDocs metadata fetch'), - ), - ); + let error; + try { + await publisher.fetchTechDocsMetadata(entityNameMock); + } catch (e) { + error = e; + } + + expect(error.message).toEqual( + expect.stringContaining('TechDocs metadata fetch'), + ); }); }); }); From 27a378b8e96907b293ad1cb4ed7ff37f1aec70b9 Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Wed, 3 Mar 2021 19:42:23 -0300 Subject: [PATCH 8/9] Update test fetchTechDocsMetadata Signed-off-by: vitorgrenzel --- .../src/stages/publish/azureBlobStorage.ts | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts index a0f512df6e..155d14fbb5 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts @@ -217,26 +217,19 @@ export class AzureBlobStoragePublish implements PublisherBase { ): Promise { const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`; try { - return await new Promise(async (resolve, reject) => { - try { - const techdocsMetadataJson = await this.download( - this.containerName, - `${entityRootDir}/techdocs_metadata.json`, - ); - if (!techdocsMetadataJson) { - throw new Error( - `Unable to parse the techdocs metadata file ${entityRootDir}/techdocs_metadata.json.`, - ); - } - const techdocsMetadata = JSON5.parse( - techdocsMetadataJson.toString('utf-8'), - ); - resolve(techdocsMetadata); - } catch (err) { - this.logger.error(err.message); - reject(new Error(err.message)); - } - }); + const techdocsMetadataJson = await this.download( + this.containerName, + `${entityRootDir}/techdocs_metadata.json`, + ); + if (!techdocsMetadataJson) { + throw new Error( + `Unable to parse the techdocs metadata file ${entityRootDir}/techdocs_metadata.json.`, + ); + } + const techdocsMetadata = JSON5.parse( + techdocsMetadataJson.toString('utf-8'), + ); + return techdocsMetadata; } catch (e) { throw new Error(`TechDocs metadata fetch failed, ${e.message}`); } From 29b2109669527f02312e2f911b0d914060554878 Mon Sep 17 00:00:00 2001 From: vitorgrenzel Date: Wed, 3 Mar 2021 19:56:52 -0300 Subject: [PATCH 9/9] Delete changeset Signed-off-by: vitorgrenzel --- .changeset/techdocs-improve-tests.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/techdocs-improve-tests.md diff --git a/.changeset/techdocs-improve-tests.md b/.changeset/techdocs-improve-tests.md deleted file mode 100644 index 4c8e68482f..0000000000 --- a/.changeset/techdocs-improve-tests.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -Improved tests fetchTechDocsMetadata to AzureBlobStorage.