From 23fade04fd460d72470b3cc7f4482f8bf73dc198 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sun, 26 Mar 2023 13:25:07 -0500 Subject: [PATCH] Completed tests Signed-off-by: Andre Wanlin --- plugins/linguist-backend/api-report.md | 3 +- .../src/api/LinguistBackendApi.test.ts | 117 +++++++++++++----- .../src/api/LinguistBackendApi.ts | 26 ++-- .../linguist-backend/src/service/router.ts | 5 +- 4 files changed, 106 insertions(+), 45 deletions(-) diff --git a/plugins/linguist-backend/api-report.md b/plugins/linguist-backend/api-report.md index ca36a1f816..907a48c50f 100644 --- a/plugins/linguist-backend/api-report.md +++ b/plugins/linguist-backend/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { CatalogApi } from '@backstage/catalog-client'; import { EntitiesOverview } from '@backstage/plugin-linguist-common'; import { EntityResults } from '@backstage/plugin-linguist-common'; import express from 'express'; @@ -31,8 +32,8 @@ export class LinguistBackendApi { logger: Logger, store: LinguistBackendStore, urlReader: UrlReader, - discovery: PluginEndpointDiscovery, tokenManager: TokenManager, + catalogApi: CatalogApi, age?: HumanDuration, batchSize?: number, useSourceLocation?: boolean, diff --git a/plugins/linguist-backend/src/api/LinguistBackendApi.test.ts b/plugins/linguist-backend/src/api/LinguistBackendApi.test.ts index baf7d27ad7..b0f20f0207 100644 --- a/plugins/linguist-backend/src/api/LinguistBackendApi.test.ts +++ b/plugins/linguist-backend/src/api/LinguistBackendApi.test.ts @@ -13,19 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { getVoidLogger, - PluginEndpointDiscovery, ReadTreeResponse, ServerTokenManager, UrlReader, } from '@backstage/backend-common'; -import { GetEntitiesResponse } from '@backstage/catalog-client'; +import { CatalogApi, GetEntitiesResponse } from '@backstage/catalog-client'; import { Results } from 'linguist-js/dist/types'; import { DateTime } from 'luxon'; import { LinguistBackendStore } from '../db'; import { kindOrDefault, LinguistBackendApi } from './LinguistBackendApi'; import fs from 'fs-extra'; +import { LINGUIST_ANNOTATION } from '@backstage/plugin-linguist-common'; const linguistResultMock = Promise.resolve({ files: { @@ -70,15 +71,6 @@ describe('kindOrDefault', () => { }); describe('Linguist backend API', () => { - const getEntitiesMock = jest.fn(); - jest.mock('@backstage/catalog-client', () => { - return { - CatalogClient: jest - .fn() - .mockImplementation(() => ({ getEntities: getEntitiesMock })), - }; - }); - const logger = getVoidLogger(); const store: jest.Mocked = { @@ -95,10 +87,10 @@ describe('Linguist backend API', () => { readUrl: jest.fn(), }; - const discovery: jest.Mocked = { - getBaseUrl: jest.fn(), - getExternalBaseUrl: jest.fn(), - }; + const catalogApi: jest.Mocked = { + getEntities: jest.fn(), + getEntityByRef: jest.fn(), + } as any; const tokenManager = ServerTokenManager.noop(); @@ -106,8 +98,8 @@ describe('Linguist backend API', () => { logger, store, urlReader, - discovery, tokenManager, + catalogApi, ); beforeEach(() => { @@ -174,7 +166,7 @@ describe('Linguist backend API', () => { }, ], }; - getEntitiesMock.mockResolvedValue(testEntityListResponse); + catalogApi.getEntities.mockResolvedValue(testEntityListResponse); await api.addNewEntities(); expect(store.insertNewEntity).toHaveBeenCalledTimes(3); @@ -211,12 +203,12 @@ describe('Linguist backend API', () => { }); it('should get entity overview with stale items', async () => { - const staleApi = new LinguistBackendApi( + const apiWithAge = new LinguistBackendApi( logger, store, urlReader, - discovery, tokenManager, + catalogApi, { days: 5 }, ); store.getProcessedEntities.mockResolvedValue([ @@ -236,7 +228,7 @@ describe('Linguist backend API', () => { 'component:default/service-five', ]); - const overview = await staleApi.getEntitiesOverview(); + const overview = await apiWithAge.getEntitiesOverview(); expect(overview.entityCount).toEqual(5); expect(overview.processedCount).toEqual(2); expect(overview.staleCount).toEqual(1); @@ -277,7 +269,7 @@ describe('Linguist backend API', () => { fsSpy.mockClear(); }); - it('should generate languages for multiple entities using default', async () => { + it('should generate languages for entities using default', async () => { store.getProcessedEntities.mockResolvedValue([ { entityRef: 'component:default/service-one', @@ -294,21 +286,56 @@ describe('Linguist backend API', () => { 'component:default/service-four', 'component:default/service-five', ]); + + const entity = { + apiVersion: 'backstage.io/v1beta1', + metadata: { + name: 'service-one', + annotations: { + [LINGUIST_ANNOTATION]: 'https://some.fake/service/', + }, + }, + kind: 'Component', + }; + + catalogApi.getEntityByRef.mockResolvedValue(entity); + + const resultsSpy = jest + .spyOn(api, 'getLinguistResults') + .mockImplementation(() => linguistResultMock); + + urlReader.readTree.mockResolvedValue({ + files: async () => [ + { + content: async () => Buffer.from('-- XXX: code-data', 'utf8'), + path: 'my-file.js', + }, + ], + dir: async () => '/temp/my-code', + } as ReadTreeResponse); + + const fsSpy = jest.spyOn(fs, 'remove'); + const generateEntityLanguages = jest.spyOn(api, 'generateEntityLanguages'); await api.generateEntitiesLanguages(); expect(generateEntityLanguages).toHaveBeenCalledTimes(3); + + generateEntityLanguages.mockClear(); + resultsSpy.mockClear(); + fsSpy.mockClear(); }); - it('should generate languages for multiple entities using defined batch size', async () => { - const batchApi = new LinguistBackendApi( + it('should generate languages for entities using defined batch size', async () => { + const apiWithBatchSize = new LinguistBackendApi( logger, store, urlReader, - discovery, tokenManager, + catalogApi, undefined, - 1, + 2, ); + store.getProcessedEntities.mockResolvedValue([ { entityRef: 'component:default/service-one', @@ -325,11 +352,45 @@ describe('Linguist backend API', () => { 'component:default/service-four', 'component:default/service-five', ]); + + const entity = { + apiVersion: 'backstage.io/v1beta1', + metadata: { + name: 'service-one', + annotations: { + [LINGUIST_ANNOTATION]: 'https://some.fake/service/', + }, + }, + kind: 'Component', + }; + + catalogApi.getEntityByRef.mockResolvedValue(entity); + + const resultsSpy = jest + .spyOn(apiWithBatchSize, 'getLinguistResults') + .mockImplementation(() => linguistResultMock); + + urlReader.readTree.mockResolvedValue({ + files: async () => [ + { + content: async () => Buffer.from('-- XXX: code-data', 'utf8'), + path: 'my-file.js', + }, + ], + dir: async () => '/temp/my-code', + } as ReadTreeResponse); + + const fsSpy = jest.spyOn(fs, 'remove'); + const generateEntityLanguages = jest.spyOn( - batchApi, + apiWithBatchSize, 'generateEntityLanguages', ); - await batchApi.generateEntitiesLanguages(); - expect(generateEntityLanguages).toHaveBeenCalledTimes(1); + await apiWithBatchSize.generateEntitiesLanguages(); + expect(generateEntityLanguages).toHaveBeenCalledTimes(2); + + generateEntityLanguages.mockClear(); + resultsSpy.mockClear(); + fsSpy.mockClear(); }); }); diff --git a/plugins/linguist-backend/src/api/LinguistBackendApi.ts b/plugins/linguist-backend/src/api/LinguistBackendApi.ts index 766012018f..f00aebcee1 100644 --- a/plugins/linguist-backend/src/api/LinguistBackendApi.ts +++ b/plugins/linguist-backend/src/api/LinguistBackendApi.ts @@ -22,14 +22,10 @@ import { } from '@backstage/plugin-linguist-common'; import { CATALOG_FILTER_EXISTS, - CatalogClient, GetEntitiesRequest, + CatalogApi, } from '@backstage/catalog-client'; -import { - PluginEndpointDiscovery, - TokenManager, - UrlReader, -} from '@backstage/backend-common'; +import { TokenManager, UrlReader } from '@backstage/backend-common'; import { DateTime } from 'luxon'; import { LINGUIST_ANNOTATION } from '@backstage/plugin-linguist-common'; @@ -49,10 +45,9 @@ export class LinguistBackendApi { private readonly logger: Logger; private readonly store: LinguistBackendStore; private readonly urlReader: UrlReader; - private readonly discovery: PluginEndpointDiscovery; private readonly tokenManager: TokenManager; - private readonly catalogClient: CatalogClient; + private readonly catalogApi: CatalogApi; private readonly age?: HumanDuration; private readonly batchSize?: number; private readonly useSourceLocation?: boolean; @@ -62,8 +57,8 @@ export class LinguistBackendApi { logger: Logger, store: LinguistBackendStore, urlReader: UrlReader, - discovery: PluginEndpointDiscovery, tokenManager: TokenManager, + catalogApi: CatalogApi, age?: HumanDuration, batchSize?: number, useSourceLocation?: boolean, @@ -73,9 +68,8 @@ export class LinguistBackendApi { this.logger = logger; this.store = store; this.urlReader = urlReader; - this.discovery = discovery; this.tokenManager = tokenManager; - this.catalogClient = new CatalogClient({ discoveryApi: this.discovery }); + this.catalogApi = catalogApi; this.batchSize = batchSize; this.age = age; this.useSourceLocation = useSourceLocation; @@ -112,7 +106,7 @@ export class LinguistBackendApi { }; const { token } = await this.tokenManager.getToken(); - const response = await this.catalogClient.getEntities(request, { token }); + const response = await this.catalogApi.getEntities(request, { token }); const entities = response.items; entities.forEach(entity => { @@ -131,9 +125,10 @@ export class LinguistBackendApi { 0, this.batchSize ?? 20, ); - entities.forEach(async entityRef => { + + for (const entityRef of entities) { const { token } = await this.tokenManager.getToken(); - const entity = await this.catalogClient.getEntityByRef(entityRef, { + const entity = await this.catalogApi.getEntityByRef(entityRef, { token, }); const annotationKey = this.useSourceLocation @@ -148,12 +143,13 @@ export class LinguistBackendApi { try { await this.generateEntityLanguages(entityRef, url); } catch (error) { + console.log(error); assertError(error); this.logger.error( `Unable to process "${entityRef}" using "${url}", message: ${error.message}, stack: ${error.stack}`, ); } - }); + } } public async getEntitiesOverview(): Promise { diff --git a/plugins/linguist-backend/src/service/router.ts b/plugins/linguist-backend/src/service/router.ts index 8d52a2e01d..4c636aa854 100644 --- a/plugins/linguist-backend/src/service/router.ts +++ b/plugins/linguist-backend/src/service/router.ts @@ -31,6 +31,7 @@ import { TaskScheduleDefinition, } from '@backstage/backend-tasks'; import { HumanDuration } from '@backstage/types'; +import { CatalogClient } from '@backstage/catalog-client'; /** @public */ export interface PluginOptions { @@ -74,14 +75,16 @@ export async function createRouter( await database.getClient(), ); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + const linguistBackendApi = routerOptions.linguistBackendApi || new LinguistBackendApi( logger, linguistBackendStore, reader, - discovery, tokenManager, + catalogClient, age, batchSize, useSourceLocation,