Merge pull request #5027 from kaos/badges_test_coverage

badges: improve test coverage.
This commit is contained in:
Ben Lambert
2021-03-31 11:09:46 +02:00
committed by GitHub
6 changed files with 142 additions and 9 deletions
+3 -3
View File
@@ -88,9 +88,9 @@ The badges backend api exposes two main endpoints for entity badges. The
[BadgeSpec](https://github.com/backstage/backstage/tree/master/plugins/badges/src/api/types.ts)
from the frontend plugin for a type declaration.
- `/badges/entity/:namespace/:kind/:name/:badgeId` Get the entity badge as an
SVG image. If the `accept` request header prefers `application/json` the badge
spec as JSON will be returned instead of the image.
- `/badges/entity/:namespace/:kind/:name/badge/:badgeId` Get the entity badge as
an SVG image. If the `accept` request header prefers `application/json` the
badge spec as JSON will be returned instead of the image.
## Links
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright 2021 Spotify AB
*
* 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 type { Entity } from '@backstage/catalog-model';
import { InputError } from '@backstage/errors';
import { Config, ConfigReader } from '@backstage/config';
import { BadgeContext, BadgeFactories } from './types';
import { createDefaultBadgeFactories } from './badges';
describe('BadgeFactories', () => {
let badgeFactories: BadgeFactories;
let config: Config;
beforeAll(() => {
badgeFactories = createDefaultBadgeFactories();
config = new ConfigReader({
app: {
baseUrl: 'http://localhost',
},
});
});
it('throws when missing entity', () => {
const context: BadgeContext = {
badgeUrl: '/dummy/url',
config,
};
expect.assertions(Object.keys(badgeFactories).length);
for (const badgeFactory of Object.values(badgeFactories)) {
expect(() => badgeFactory.createBadge(context)).toThrow(InputError);
}
});
it('returns valid badge for entity', () => {
const entity: Entity = {
apiVersion: 'v1',
kind: 'service',
metadata: {
name: 'test',
},
};
const context: BadgeContext = {
badgeUrl: '/dummy/url',
config,
entity,
};
expect.assertions(Object.keys(badgeFactories).length);
for (const badgeFactory of Object.values(badgeFactories)) {
const badge = badgeFactory.createBadge(context);
expect(badge.kind).toEqual('entity');
}
});
});
+1 -1
View File
@@ -19,7 +19,7 @@ import { InputError } from '@backstage/errors';
import { Badge, BadgeContext, BadgeFactories } from './types';
function appTitle(context: BadgeContext): string {
return context.config.getString('app.title') || 'Backstage';
return context.config.getOptionalString('app.title') || 'Backstage';
}
function entityUrl(context: BadgeContext): string {
@@ -15,9 +15,10 @@
*/
import { Config, ConfigReader } from '@backstage/config';
import { InputError } from '@backstage/errors';
import { DefaultBadgeBuilder } from './DefaultBadgeBuilder';
import { BadgeBuilder, BadgeOptions } from './types';
import { BadgeContext, BadgeFactories } from '../../types';
import { Badge, BadgeContext, BadgeFactories, BadgeStyle } from '../../types';
describe('DefaultBadgeBuilder', () => {
let builder: BadgeBuilder;
@@ -40,6 +41,12 @@ describe('DefaultBadgeBuilder', () => {
testbadge: {
createBadge: () => badge,
},
failbadge: {
createBadge: () => (undefined as unknown) as Badge, // force a bad return value..
},
invalidbadge: {
createBadge: () => ({ style: 'wrong' as BadgeStyle, ...badge }),
},
};
});
@@ -49,7 +56,11 @@ describe('DefaultBadgeBuilder', () => {
});
it('getBadges() returns all badge factory ids', async () => {
expect(await builder.getBadges()).toEqual([{ id: 'testbadge' }]);
expect(await builder.getBadges()).toEqual([
{ id: 'testbadge' },
{ id: 'failbadge' },
{ id: 'invalidbadge' },
]);
});
describe('createBadge[Json|Svg]', () => {
@@ -101,5 +112,25 @@ describe('DefaultBadgeBuilder', () => {
markdown: `![unknown badge: other-id](${context.badgeUrl})`,
});
});
it('throws InputError when the BadgeFactory.createBadge() fails', async () => {
expect.assertions(1);
await expect(
builder.createBadgeJson({ badgeInfo: { id: 'failbadge' }, context }),
).rejects.toEqual(
new InputError(
'The badge factory failed to produce a "failbadge" badge with the provided context',
),
);
});
it('returns "invalid badge" for bad parameters', async () => {
expect(
await builder.createBadgeSvg({
badgeInfo: { id: 'invalidbadge' },
context,
}),
).toEqual(expect.stringMatching(/Error: Field `style` must be one of/));
});
});
});
@@ -169,5 +169,41 @@ describe('createRouter', () => {
},
});
});
it('returns badge spec for entity', async () => {
catalog.getEntityByName.mockResolvedValueOnce(entity);
badgeBuilder.createBadgeJson.mockResolvedValueOnce(badge);
const url = '/entity/default/service/test/badge/test-badge?format=json';
const response = await request(app).get(url);
expect(response.status).toEqual(200);
expect(response.body).toEqual(badge);
});
});
describe('Errors', () => {
it('returns 404 for unknown entities', async () => {
catalog.getEntityByName.mockResolvedValue(undefined);
async function testUrl(url: string) {
const response = await request(app).get(url);
expect(response.status).toEqual(404);
expect(response.body).toEqual({
error: {
message: 'No service entity in default named "missing"',
name: 'NotFoundError',
},
request: {
method: 'GET',
url,
},
response: {
statusCode: 404,
},
});
}
await testUrl('/entity/default/service/missing/badge-specs');
await testUrl('/entity/default/service/missing/badge/test-badge');
});
});
});
+1 -3
View File
@@ -68,9 +68,7 @@ export async function createRouter(
};
const badge = await badgeBuilder.createBadgeJson({ badgeInfo, context });
if (badge) {
specs.push(badge);
}
specs.push(badge);
}
res.setHeader('Content-Type', 'application/json');