test(org): add queryParams coverage for OwnershipCard useGetEntities

The existing tests never exercised `getQueryParams` because the mock
catalog API always returned empty items. These new tests verify that the
generated query parameters include correctly humanized owner entity refs,
handle multiple owners, and group entities by kind and type.

Made-with: Cursor
Signed-off-by: Marat Dyatko <maratd@spotify.com>
Made-with: Cursor
This commit is contained in:
Marat Dyatko
2026-04-09 10:24:04 +02:00
parent aba2f0a980
commit 09e17c8a65
@@ -18,6 +18,7 @@ import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
import { useGetEntities } from './useGetEntities';
import { renderHook, waitFor } from '@testing-library/react';
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
import qs from 'qs';
const givenParentGroup = 'team.squad1';
const givenLeafGroup = 'team.squad2';
@@ -45,8 +46,11 @@ const getEntityRelationsMock: jest.Mock<
[Entity | undefined]
> = jest.fn();
jest.mock('@backstage/plugin-catalog-react', () => {
const actual = jest.requireActual('@backstage/plugin-catalog-react');
return {
catalogApiRef: {},
entityPresentationSnapshot: actual.entityPresentationSnapshot,
humanizeEntityRef: actual.humanizeEntityRef,
getEntityRelations: jest.fn(entity => {
return getEntityRelationsMock(entity);
}) as any,
@@ -283,6 +287,143 @@ describe('useGetEntities', () => {
});
});
describe('queryParams generation', () => {
afterEach(() => {
getEntityRelationsMock.mockRestore();
catalogApi.getEntities.mockRestore();
});
it('should produce query params with humanized entity refs as owners', async () => {
getEntityRelationsMock.mockReturnValue([]);
catalogApi.getEntities.mockResolvedValueOnce({
items: [
{
kind: 'Component',
metadata: { name: 'my-service', namespace: 'default' },
spec: { type: 'service' },
} as Partial<Entity> as Entity,
],
});
const { result } = renderHook(
({ entity }) => useGetEntities(entity, 'direct'),
{ initialProps: { entity: givenLeafGroupEntity } },
);
await waitFor(() => expect(result.current.loading).toBe(false));
expect(result.current.componentsWithCounters).toHaveLength(1);
const params = qs.parse(
result.current.componentsWithCounters![0].queryParams,
);
expect(params).toEqual({
filters: {
kind: 'component',
type: 'service',
owners: givenLeafGroup,
user: 'all',
},
});
});
it('should include multiple owners as an array in query params', async () => {
getEntityRelationsMock
.mockReturnValueOnce([createGroupRefFromName(givenLeafGroup)])
.mockReturnValue([]);
catalogApi.getEntitiesByRefs.mockResolvedValueOnce({
items: [givenLeafGroupEntity],
});
catalogApi.getEntities.mockResolvedValueOnce({
items: [
{
kind: 'API',
metadata: { name: 'my-api', namespace: 'default' },
spec: { type: 'openapi' },
} as Partial<Entity> as Entity,
],
});
const { result } = renderHook(
({ entity }) => useGetEntities(entity, 'aggregated'),
{ initialProps: { entity: givenParentGroupEntity } },
);
await waitFor(() => expect(result.current.loading).toBe(false));
expect(result.current.componentsWithCounters).toHaveLength(1);
const params = qs.parse(
result.current.componentsWithCounters![0].queryParams,
);
expect(params).toEqual({
filters: {
kind: 'api',
type: 'openapi',
owners: expect.arrayContaining([givenLeafGroup, givenParentGroup]),
user: 'all',
},
});
});
it('should group entities by kind and type in query params', async () => {
getEntityRelationsMock.mockReturnValue([]);
catalogApi.getEntities.mockResolvedValueOnce({
items: [
{
kind: 'Component',
metadata: { name: 'svc-1', namespace: 'default' },
spec: { type: 'service' },
} as Partial<Entity> as Entity,
{
kind: 'Component',
metadata: { name: 'svc-2', namespace: 'default' },
spec: { type: 'service' },
} as Partial<Entity> as Entity,
{
kind: 'API',
metadata: { name: 'api-1', namespace: 'default' },
spec: { type: 'openapi' },
} as Partial<Entity> as Entity,
],
});
const { result } = renderHook(
({ entity }) => useGetEntities(entity, 'direct'),
{ initialProps: { entity: givenLeafGroupEntity } },
);
await waitFor(() => expect(result.current.loading).toBe(false));
expect(result.current.componentsWithCounters).toHaveLength(2);
const componentEntry = result.current.componentsWithCounters!.find(
c => c.kind === 'Component',
)!;
expect(componentEntry.counter).toBe(2);
const componentParams = qs.parse(componentEntry.queryParams);
expect(componentParams).toEqual({
filters: expect.objectContaining({
kind: 'component',
type: 'service',
}),
});
const apiEntry = result.current.componentsWithCounters!.find(
c => c.kind === 'API',
)!;
expect(apiEntry.counter).toBe(1);
const apiParams = qs.parse(apiEntry.queryParams);
expect(apiParams).toEqual({
filters: expect.objectContaining({
kind: 'api',
type: 'openapi',
}),
});
});
});
describe('useGetEntities exceeding default 16384 bytes header size', () => {
const largeNumberOfGroups = Array.from({ length: 600 }, (_, i) =>
createGroupEntityFromName(`very-long-group-name-${i + 1}`),