Completed tests

Signed-off-by: Andre Wanlin <andrewanlin@gmail.com>
This commit is contained in:
Andre Wanlin
2023-03-26 13:25:07 -05:00
parent 72b27b1bf6
commit 23fade04fd
4 changed files with 106 additions and 45 deletions
+2 -1
View File
@@ -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,
@@ -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<LinguistBackendStore> = {
@@ -95,10 +87,10 @@ describe('Linguist backend API', () => {
readUrl: jest.fn(),
};
const discovery: jest.Mocked<PluginEndpointDiscovery> = {
getBaseUrl: jest.fn(),
getExternalBaseUrl: jest.fn(),
};
const catalogApi: jest.Mocked<CatalogApi> = {
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();
});
});
@@ -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<EntitiesOverview> {
@@ -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,