Skip to content

Commit

Permalink
Merge pull request #5027 from kaos/badges_test_coverage
Browse files Browse the repository at this point in the history
badges: improve test coverage.
  • Loading branch information
benjdlambert committed Mar 31, 2021
2 parents afd4512 + 2be7cf0 commit d53d01d
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 9 deletions.
6 changes: 3 additions & 3 deletions plugins/badges-backend/README.md
Expand Up @@ -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

Expand Down
68 changes: 68 additions & 0 deletions plugins/badges-backend/src/badges.test.ts
@@ -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');
}
});
});
2 changes: 1 addition & 1 deletion plugins/badges-backend/src/badges.ts
Expand Up @@ -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 {
Expand Down
Expand Up @@ -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;
Expand All @@ -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 }),
},
};
});

Expand All @@ -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]', () => {
Expand Down Expand Up @@ -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/));
});
});
});
36 changes: 36 additions & 0 deletions plugins/badges-backend/src/service/router.test.ts
Expand Up @@ -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');
});
});
});
4 changes: 1 addition & 3 deletions plugins/badges-backend/src/service/router.ts
Expand Up @@ -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');
Expand Down

0 comments on commit d53d01d

Please sign in to comment.