use mockServices more

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2024-12-18 14:14:42 +01:00
parent 525db55989
commit ee8b63def9
14 changed files with 79 additions and 150 deletions
@@ -28,16 +28,12 @@ import {
textPlaceholderResolver,
yamlPlaceholderResolver,
} from './PlaceholderProcessor';
import { UrlReaderService } from '@backstage/backend-plugin-api';
import { mockServices } from '@backstage/backend-test-utils';
const integrations = ScmIntegrations.fromConfig(new ConfigReader({}));
describe('PlaceholderProcessor', () => {
const reader: jest.Mocked<UrlReaderService> = {
readTree: jest.fn(),
search: jest.fn(),
readUrl: jest.fn(),
};
const reader = mockServices.urlReader.mock();
beforeEach(() => {
jest.resetAllMocks();
@@ -31,7 +31,6 @@ import {
import { defaultEntityDataParser } from '../util/parse';
import { UrlReaderProcessor } from './UrlReaderProcessor';
import { UrlReaders } from '@backstage/backend-defaults/urlReader';
import { UrlReaderService } from '@backstage/backend-plugin-api';
describe('UrlReaderProcessor', () => {
const mockApiOrigin = 'http://localhost';
@@ -193,11 +192,9 @@ describe('UrlReaderProcessor', () => {
it('uses search when there are globs', async () => {
const logger = mockServices.logger.mock();
const reader: jest.Mocked<UrlReaderService> = {
readUrl: jest.fn(),
readTree: jest.fn(),
search: jest.fn().mockImplementation(async () => []),
};
const reader = mockServices.urlReader.mock({
search: jest.fn().mockResolvedValue([]),
});
const processor = new UrlReaderProcessor({ reader, logger });
@@ -42,7 +42,6 @@ import { wrapServer } from '@backstage/backend-openapi-utils';
import { Server } from 'http';
import { mockCredentials, mockServices } from '@backstage/backend-test-utils';
import { LocationAnalyzer } from '@backstage/plugin-catalog-node';
import { PermissionsService } from '@backstage/backend-plugin-api';
describe('createRouter readonly disabled', () => {
let entitiesCatalog: jest.Mocked<EntitiesCatalog>;
@@ -51,7 +50,7 @@ describe('createRouter readonly disabled', () => {
let app: express.Express | Server;
let refreshService: RefreshService;
let locationAnalyzer: jest.Mocked<LocationAnalyzer>;
let permissionsService: jest.Mocked<PermissionsService>;
const permissionsService = mockServices.permissions.mock();
beforeEach(async () => {
entitiesCatalog = {
@@ -69,15 +68,9 @@ describe('createRouter readonly disabled', () => {
deleteLocation: jest.fn(),
getLocationByEntity: jest.fn(),
};
locationAnalyzer = {
analyzeLocation: jest.fn(),
};
permissionsService = {
authorize: jest.fn(),
authorizeConditional: jest.fn(),
};
refreshService = { refresh: jest.fn() };
orchestrator = { process: jest.fn() };
const router = await createRouter({
@@ -91,13 +84,13 @@ describe('createRouter readonly disabled', () => {
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
locationAnalyzer,
permissionsService: permissionsService,
permissionsService,
});
app = await wrapServer(express().use(router));
});
beforeEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
describe('POST /refresh', () => {
@@ -158,7 +151,7 @@ describe('createRouter readonly disabled', () => {
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
locationAnalyzer,
permissionsService: permissionsService,
permissionsService,
disableRelationsCompatibility: true, // added
});
app = await wrapServer(express().use(router));
@@ -218,7 +211,7 @@ describe('createRouter readonly disabled', () => {
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
locationAnalyzer,
permissionsService: permissionsService,
permissionsService,
disableRelationsCompatibility: true, // added
});
app = await wrapServer(express().use(router));
@@ -939,7 +932,7 @@ describe('createRouter readonly and raw json enabled', () => {
let entitiesCatalog: jest.Mocked<EntitiesCatalog>;
let app: express.Express;
let locationService: jest.Mocked<LocationService>;
let permissionsService: jest.Mocked<PermissionsService>;
const permissionsService = mockServices.permissions.mock();
beforeAll(async () => {
entitiesCatalog = {
@@ -970,13 +963,13 @@ describe('createRouter readonly and raw json enabled', () => {
permissionIntegrationRouter: express.Router(),
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
permissionsService: permissionsService,
permissionsService,
});
app = express().use(router);
});
beforeEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
describe('GET /entities', () => {
@@ -1141,7 +1134,7 @@ describe('NextRouter permissioning', () => {
let locationService: jest.Mocked<LocationService>;
let app: express.Express;
let refreshService: RefreshService;
let permissionsService: jest.Mocked<PermissionsService>;
const permissionsService = mockServices.permissions.mock();
const fakeRule = createPermissionRule({
name: 'FAKE_RULE',
@@ -1188,13 +1181,13 @@ describe('NextRouter permissioning', () => {
}),
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
permissionsService: permissionsService,
permissionsService,
});
app = express().use(router);
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
it('accepts and evaluates conditions at the apply-conditions endpoint', async () => {
@@ -23,10 +23,7 @@ import {
registerMswTestHooks,
} from '@backstage/backend-test-utils';
import { NotFoundError } from '@backstage/errors';
import {
AuthorizeResult,
PermissionEvaluator,
} from '@backstage/plugin-permission-common';
import { AuthorizeResult } from '@backstage/plugin-permission-common';
import {
ANNOTATION_KUBERNETES_AUTH_PROVIDER,
KubernetesRequestAuth,
@@ -55,10 +52,7 @@ import {
} from './KubernetesProxy';
import type { Request } from 'express';
import {
BackstageCredentials,
DiscoveryService,
} from '@backstage/backend-plugin-api';
import { BackstageCredentials } from '@backstage/backend-plugin-api';
const mockCertDir = createMockDirectory({
content: {
@@ -79,15 +73,8 @@ describe('KubernetesProxy', () => {
>(),
};
const permissionApi: jest.Mocked<PermissionEvaluator> = {
authorize: jest.fn(),
authorizeConditional: jest.fn(),
};
const mockDisocveryApi: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn(),
getExternalBaseUrl: jest.fn(),
};
const permissionApi = mockServices.permissions.mock();
const mockDisocveryApi = mockServices.discovery.mock();
registerMswTestHooks(worker);
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConfigReader } from '@backstage/config';
import { TestPipeline } from '@backstage/plugin-search-backend-node';
import {
mockServices,
@@ -23,7 +23,6 @@ import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { Readable } from 'stream';
import { ToolDocumentCollatorFactory } from './ToolDocumentCollatorFactory';
import { DiscoveryService } from '@backstage/backend-plugin-api';
const logger = mockServices.logger.mock();
@@ -54,11 +53,10 @@ const mockTools = {
};
describe('ToolDocumentCollatorFactory', () => {
const config = new ConfigReader({});
const mockDiscoveryApi: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn().mockResolvedValue('http://test-backend/api/explore'),
getExternalBaseUrl: jest.fn(),
};
const config = mockServices.rootConfig();
const mockDiscoveryApi = mockServices.discovery.mock({
getBaseUrl: async () => 'http://test-backend/api/explore',
});
const options = {
discovery: mockDiscoveryApi,
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Entity } from '@backstage/catalog-model';
import { ConfigReader } from '@backstage/config';
import { TestPipeline } from '@backstage/plugin-search-backend-node';
@@ -25,7 +26,6 @@ import { setupServer } from 'msw/node';
import { Readable } from 'stream';
import { DefaultTechDocsCollatorFactory } from './DefaultTechDocsCollatorFactory';
import { TechDocsCollatorEntityTransformer } from './TechDocsCollatorEntityTransformer';
import { DiscoveryService } from '@backstage/backend-plugin-api';
import {
MkSearchIndexDoc,
TechDocsCollatorDocumentTransformer,
@@ -81,11 +81,10 @@ const expectedEntities: Entity[] = [
];
describe('DefaultTechDocsCollatorFactory', () => {
const config = new ConfigReader({});
const mockDiscoveryApi: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'),
getExternalBaseUrl: jest.fn(),
};
const config = mockServices.rootConfig.mock();
const mockDiscoveryApi = mockServices.discovery.mock({
getBaseUrl: async () => 'http://test-backend',
});
const options = {
logger,
discovery: mockDiscoveryApi,
@@ -18,27 +18,14 @@ import express from 'express';
import request from 'supertest';
import { createRouter } from './router';
import { EventsService } from '@backstage/plugin-events-node';
import {
DiscoveryService,
UserInfoService,
} from '@backstage/backend-plugin-api';
import { ConfigReader } from '@backstage/config';
import { mockErrorHandler, mockServices } from '@backstage/backend-test-utils';
const eventsServiceMock: jest.Mocked<EventsService> = {
subscribe: jest.fn(),
publish: jest.fn(),
};
const discovery: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn().mockResolvedValue('/api/signals'),
getExternalBaseUrl: jest.fn(),
};
const userInfo: jest.Mocked<UserInfoService> = {
getUserInfo: jest.fn(),
};
const eventsServiceMock = mockServices.events.mock();
const discovery = mockServices.discovery.mock({
getBaseUrl: async () => '/api/signals',
});
const userInfo = mockServices.userInfo.mock();
describe('createRouter', () => {
let app: express.Express;
+24 -33
View File
@@ -17,7 +17,6 @@
import { ConfigReader } from '@backstage/config';
import { CacheInvalidationError, TechDocsCache } from './TechDocsCache';
import { mockServices } from '@backstage/backend-test-utils';
import { CacheService } from '@backstage/backend-plugin-api';
const cached = (str: string): string => {
return Buffer.from(str).toString('base64');
@@ -25,17 +24,12 @@ const cached = (str: string): string => {
describe('TechDocsCache', () => {
let CacheUnderTest: TechDocsCache;
let MockClient: jest.Mocked<CacheService>;
const cache = mockServices.cache.mock();
beforeEach(() => {
MockClient = {
get: jest.fn(),
set: jest.fn(),
delete: jest.fn(),
withOptions: jest.fn(),
};
jest.clearAllMocks();
CacheUnderTest = TechDocsCache.fromConfig(new ConfigReader({}), {
cache: MockClient,
cache: cache,
logger: mockServices.logger.mock(),
});
});
@@ -43,16 +37,16 @@ describe('TechDocsCache', () => {
describe('get', () => {
it('returns undefined if no response', async () => {
const expectedPath = 'some/index.html';
MockClient.get.mockResolvedValueOnce(undefined);
cache.get.mockResolvedValueOnce(undefined);
const actual = await CacheUnderTest.get(expectedPath);
expect(MockClient.get).toHaveBeenCalledWith(expectedPath);
expect(cache.get).toHaveBeenCalledWith(expectedPath);
expect(actual).toBe(undefined);
});
it('returns undefined if cache get throws', async () => {
const expectedPath = 'some/index.html';
MockClient.get.mockRejectedValueOnce(new Error());
cache.get.mockRejectedValueOnce(new Error());
const actual = await CacheUnderTest.get(expectedPath);
expect(actual).toBe(undefined);
@@ -60,7 +54,7 @@ describe('TechDocsCache', () => {
it('returns undefined if no response after 1s by default', async () => {
const expectedPath = 'some/index.html';
MockClient.get.mockImplementationOnce(() => {
cache.get.mockImplementationOnce(() => {
return new Promise(resolve => {
setTimeout(() => resolve(cached('value')), 1500);
});
@@ -71,7 +65,7 @@ describe('TechDocsCache', () => {
it('returns undefined if no response after configured readTimeout', async () => {
const expectedPath = 'some/index.html';
MockClient.get.mockImplementationOnce(() => {
cache.get.mockImplementationOnce(() => {
return new Promise(resolve => {
setTimeout(() => resolve(cached('value')), 20);
});
@@ -82,7 +76,7 @@ describe('TechDocsCache', () => {
techdocs: { cache: { readTimeout: 10 } },
}),
{
cache: MockClient,
cache: cache,
logger: mockServices.logger.mock(),
},
);
@@ -93,7 +87,7 @@ describe('TechDocsCache', () => {
it('returns data if cache get returns it', async () => {
const expectedPath = 'some/index.html';
MockClient.get.mockResolvedValueOnce(cached('expected value'));
cache.get.mockResolvedValueOnce(cached('expected value'));
const actual = await CacheUnderTest.get(expectedPath);
expect(actual?.toString()).toBe('expected value');
@@ -103,17 +97,14 @@ describe('TechDocsCache', () => {
describe('set', () => {
it('sets a base64-encoded string', async () => {
const expectedPath = 'some/index.html';
MockClient.set.mockResolvedValueOnce(undefined);
cache.set.mockResolvedValueOnce(undefined);
await CacheUnderTest.set(expectedPath, Buffer.from('some data'));
expect(MockClient.set).toHaveBeenCalledWith(
expectedPath,
cached('some data'),
);
expect(cache.set).toHaveBeenCalledWith(expectedPath, cached('some data'));
});
it('does not throw if client throws', () => {
MockClient.set.mockRejectedValueOnce(new Error());
cache.set.mockRejectedValueOnce(new Error());
expect(() => CacheUnderTest.set('i.html', Buffer.from(''))).not.toThrow();
});
});
@@ -121,26 +112,26 @@ describe('TechDocsCache', () => {
describe('invalidate', () => {
it('calls delete on client', async () => {
const expectedPath = 'some/index.html';
MockClient.delete.mockResolvedValueOnce(undefined);
cache.delete.mockResolvedValueOnce(undefined);
await CacheUnderTest.invalidate(expectedPath);
expect(MockClient.delete).toHaveBeenCalledWith(expectedPath);
expect(cache.delete).toHaveBeenCalledWith(expectedPath);
});
});
describe('invalidateMultiple', () => {
it('calls delete once per given path', async () => {
const expectedPaths = ['one/index.html', 'two/index.html'];
MockClient.delete.mockResolvedValue(undefined);
cache.delete.mockResolvedValue(undefined);
await CacheUnderTest.invalidateMultiple(expectedPaths);
expect(MockClient.delete).toHaveBeenNthCalledWith(1, expectedPaths[0]);
expect(MockClient.delete).toHaveBeenNthCalledWith(2, expectedPaths[1]);
expect(cache.delete).toHaveBeenNthCalledWith(1, expectedPaths[0]);
expect(cache.delete).toHaveBeenNthCalledWith(2, expectedPaths[1]);
});
it('returns an array of as many paths provided', async () => {
const expectedPaths = ['one/index.html', 'two/index.html'];
MockClient.delete.mockResolvedValue(undefined);
cache.delete.mockResolvedValue(undefined);
const actual = await CacheUnderTest.invalidateMultiple(expectedPaths);
expect(actual.length).toBe(2);
@@ -148,19 +139,19 @@ describe('TechDocsCache', () => {
it('calls delete on all paths even if the first rejects', async () => {
const expectedPaths = ['one/index.html', 'two/index.html'];
MockClient.delete.mockRejectedValueOnce(new Error());
MockClient.delete.mockResolvedValueOnce(undefined);
cache.delete.mockRejectedValueOnce(new Error());
cache.delete.mockResolvedValueOnce(undefined);
await expect(
CacheUnderTest.invalidateMultiple(expectedPaths),
).rejects.toThrow(CacheInvalidationError);
expect(MockClient.delete).toHaveBeenCalledTimes(2);
expect(cache.delete).toHaveBeenCalledTimes(2);
});
it('rejects with invalidations error response', async () => {
const expectedPaths = ['one/index.html', 'two/index.html'];
MockClient.delete.mockResolvedValueOnce(undefined);
MockClient.delete.mockRejectedValueOnce(new Error());
cache.delete.mockResolvedValueOnce(undefined);
cache.delete.mockRejectedValueOnce(new Error());
await expect(
CacheUnderTest.invalidateMultiple.bind(CacheUnderTest, expectedPaths),
@@ -25,7 +25,6 @@ import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
import { ConfigReader } from '@backstage/config';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import { DiscoveryService } from '@backstage/backend-plugin-api';
const logger = loggerToWinstonLogger(mockServices.logger.mock());
@@ -80,15 +79,14 @@ describe('TechDocs Collator', () => {
registerMswTestHooks(worker);
describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => {
let mockDiscoveryApi: jest.Mocked<DiscoveryService>;
const mockDiscoveryApi = mockServices.discovery.mock({
getBaseUrl: async () => 'http://test-backend',
});
let mockTokenManager: jest.Mocked<TokenManager>;
let collator: DefaultTechDocsCollator;
beforeEach(() => {
mockDiscoveryApi = {
getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'),
getExternalBaseUrl: jest.fn(),
};
jest.clearAllMocks();
mockTokenManager = {
getToken: jest.fn().mockResolvedValue({ token: '' }),
authenticate: jest.fn(),
@@ -144,15 +142,13 @@ describe('TechDocs Collator', () => {
});
describe('DefaultTechDocsCollator', () => {
let mockDiscoveryApi: jest.Mocked<DiscoveryService>;
const mockDiscoveryApi = mockServices.discovery.mock({
getBaseUrl: async () => 'http://test-backend',
});
let mockTokenManager: jest.Mocked<TokenManager>;
let collator: DefaultTechDocsCollator;
beforeEach(() => {
mockDiscoveryApi = {
getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'),
getExternalBaseUrl: jest.fn(),
};
mockTokenManager = {
getToken: jest.fn().mockResolvedValue({ token: '' }),
authenticate: jest.fn(),
@@ -31,7 +31,6 @@ import {
mockServices,
registerMswTestHooks,
} from '@backstage/backend-test-utils';
import { DiscoveryService } from '@backstage/backend-plugin-api';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
@@ -59,10 +58,7 @@ describe('DocsSynchronizer', () => {
hasDocsBeenGenerated: jest.fn(),
publish: jest.fn(),
};
const discovery: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn(),
getExternalBaseUrl: jest.fn(),
};
const discovery = mockServices.discovery.mock();
const cache: jest.Mocked<TechDocsCache> = {
get: jest.fn(),
set: jest.fn(),
@@ -32,7 +32,6 @@ import { CachedEntityLoader } from './CachedEntityLoader';
import { createEventStream, createRouter, RouterOptions } from './router';
import { TechDocsCache } from '../cache';
import { mockErrorHandler, mockServices } from '@backstage/backend-test-utils';
import { DiscoveryService } from '@backstage/backend-plugin-api';
jest.mock('@backstage/catalog-client');
jest.mock('@backstage/config');
@@ -111,10 +110,7 @@ describe('createRouter', () => {
hasDocsBeenGenerated: jest.fn(),
publish: jest.fn(),
};
const discovery: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn(),
getExternalBaseUrl: jest.fn(),
};
const discovery = mockServices.discovery.mock();
const cache: jest.Mocked<PluginCacheManager> = {
getClient: jest.fn(),
};
@@ -13,11 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import { ConfigReader } from '@backstage/config';
import { DirectoryPreparer } from './dir';
import { mockServices } from '@backstage/backend-test-utils';
import { UrlReaderService } from '@backstage/backend-plugin-api';
function normalizePath(path: string) {
return path
@@ -45,12 +44,8 @@ const createMockEntity = (annotations: {}) => {
};
};
const mockConfig = new ConfigReader({});
const mockUrlReader: jest.Mocked<UrlReaderService> = {
readUrl: jest.fn(),
readTree: jest.fn(),
search: jest.fn(),
};
const mockConfig = mockServices.rootConfig();
const mockUrlReader = mockServices.urlReader.mock();
describe('directory preparer', () => {
it('should merge managed-by-location and techdocs-ref when techdocs-ref is relative', async () => {
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { overridePackagePathResolution } from '@backstage/backend-plugin-api/testUtils';
import { ConfigReader } from '@backstage/config';
import express from 'express';
@@ -24,7 +25,6 @@ import {
createMockDirectory,
mockServices,
} from '@backstage/backend-test-utils';
import { DiscoveryService } from '@backstage/backend-plugin-api';
const createMockEntity = (annotations = {}, lowerCase = false) => {
return {
@@ -39,10 +39,9 @@ const createMockEntity = (annotations = {}, lowerCase = false) => {
};
};
const testDiscovery: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007/api/techdocs'),
getExternalBaseUrl: jest.fn(),
};
const testDiscovery = mockServices.discovery.mock({
getBaseUrl: async () => 'http://localhost:7007/api/techdocs',
});
const mockPublishDir = createMockDirectory();
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConfigReader } from '@backstage/config';
import { Publisher } from './publish';
import { LocalPublish } from './local';
@@ -22,13 +23,11 @@ import { AzureBlobStoragePublish } from './azureBlobStorage';
import { OpenStackSwiftPublish } from './openStackSwift';
import { mockServices } from '@backstage/backend-test-utils';
import { PublisherBase } from './types';
import { DiscoveryService } from '@backstage/backend-plugin-api';
const logger = mockServices.logger.mock();
const discovery: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7007'),
getExternalBaseUrl: jest.fn(),
};
const discovery = mockServices.discovery.mock({
getBaseUrl: async () => 'http://localhost:7007',
});
jest.mock('@azure/identity', () => ({
__esModule: true,