test: add tests for notification clients
Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
@@ -184,7 +184,7 @@ export async function createRouter(
|
||||
response.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
router.get('/notifications', async (req, res) => {
|
||||
router.get('/', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const opts: NotificationGetOptions = {
|
||||
user: user,
|
||||
@@ -274,7 +274,7 @@ export async function createRouter(
|
||||
// Add new notification
|
||||
// Allowed only for service-to-service authentication, uses `getUsersForEntityRef` to retrieve recipients for
|
||||
// specific entity reference
|
||||
router.post('/notifications', async (req, res) => {
|
||||
router.post('/', async (req, res) => {
|
||||
const { recipients, origin, payload } = req.body;
|
||||
const notifications = [];
|
||||
let users = [];
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"msw": "^1.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* 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 { setupRequestMockHandlers } from '@backstage/test-utils';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { NotificationPayload } from '@backstage/plugin-notifications-common';
|
||||
import {
|
||||
DefaultNotificationService,
|
||||
NotificationSendOptions,
|
||||
} from './DefaultNotificationService';
|
||||
|
||||
const server = setupServer();
|
||||
|
||||
const testNotification: NotificationPayload = {
|
||||
title: 'Notification 1',
|
||||
link: '/catalog',
|
||||
severity: 'normal',
|
||||
};
|
||||
|
||||
describe('DefaultNotificationService', () => {
|
||||
setupRequestMockHandlers(server);
|
||||
const mockBaseUrl = 'http://backstage/api/notifications';
|
||||
const discoveryApi = {
|
||||
getBaseUrl: async () => mockBaseUrl,
|
||||
getExternalBaseUrl: async () => mockBaseUrl,
|
||||
};
|
||||
const tokenManager = {
|
||||
getToken: async () => ({ token: '1234' }),
|
||||
authenticate: jest.fn(),
|
||||
};
|
||||
|
||||
let service: DefaultNotificationService;
|
||||
beforeEach(() => {
|
||||
service = DefaultNotificationService.create({
|
||||
discovery: discoveryApi,
|
||||
tokenManager,
|
||||
pluginId: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNotifications', () => {
|
||||
it('should create notification', async () => {
|
||||
const body: NotificationSendOptions = {
|
||||
recipients: { type: 'entity', entityRef: ['user:default/john.doe'] },
|
||||
payload: testNotification,
|
||||
};
|
||||
|
||||
server.use(
|
||||
rest.post(`${mockBaseUrl}/`, async (req, res, ctx) => {
|
||||
const json = await req.json();
|
||||
expect(json).toEqual({ ...body, origin: 'plugin-test' });
|
||||
expect(req.headers.get('Authorization')).toEqual('Bearer 1234');
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
);
|
||||
await expect(service.send(body)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw error if failing', async () => {
|
||||
const body: NotificationSendOptions = {
|
||||
recipients: { type: 'entity', entityRef: ['user:default/john.doe'] },
|
||||
payload: testNotification,
|
||||
};
|
||||
|
||||
server.use(
|
||||
rest.post(`${mockBaseUrl}/`, async (req, res, ctx) => {
|
||||
const json = await req.json();
|
||||
expect(json).toEqual({ ...body, origin: 'plugin-test' });
|
||||
expect(req.headers.get('Authorization')).toEqual('Bearer 1234');
|
||||
return res(ctx.status(400));
|
||||
}),
|
||||
);
|
||||
await expect(service.send(body)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -60,7 +60,7 @@ export class DefaultNotificationService implements NotificationService {
|
||||
try {
|
||||
const baseUrl = await this.discovery.getBaseUrl('notifications');
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
await fetch(`${baseUrl}/notifications`, {
|
||||
const response = await fetch(`${baseUrl}/`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
...notification,
|
||||
@@ -69,9 +69,14 @@ export class DefaultNotificationService implements NotificationService {
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Request failed with status ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
// TODO: Should not throw in optimal case, see BEP
|
||||
throw new Error(`Failed to send notifications: ${error}`);
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* 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 { MockFetchApi, setupRequestMockHandlers } from '@backstage/test-utils';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { NotificationsClient } from './NotificationsClient';
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
|
||||
const server = setupServer();
|
||||
|
||||
const testNotification: Partial<Notification> = {
|
||||
user: 'user:default/john.doe',
|
||||
origin: 'plugin-test',
|
||||
payload: {
|
||||
title: 'Notification 1',
|
||||
link: '/catalog',
|
||||
severity: 'normal',
|
||||
},
|
||||
};
|
||||
|
||||
describe('NotificationsClient', () => {
|
||||
setupRequestMockHandlers(server);
|
||||
const mockBaseUrl = 'http://backstage/api/notifications';
|
||||
const discoveryApi = { getBaseUrl: async () => mockBaseUrl };
|
||||
const fetchApi = new MockFetchApi();
|
||||
|
||||
let client: NotificationsClient;
|
||||
beforeEach(() => {
|
||||
client = new NotificationsClient({ discoveryApi, fetchApi });
|
||||
});
|
||||
|
||||
describe('getNotifications', () => {
|
||||
const expectedResp = [testNotification];
|
||||
|
||||
it('should fetch notifications from correct endpoint', async () => {
|
||||
server.use(
|
||||
rest.get(`${mockBaseUrl}/`, (_, res, ctx) =>
|
||||
res(ctx.json(expectedResp)),
|
||||
),
|
||||
);
|
||||
const response = await client.getNotifications();
|
||||
expect(response).toEqual(expectedResp);
|
||||
});
|
||||
|
||||
it('should fetch notifications with options', async () => {
|
||||
server.use(
|
||||
rest.get(`${mockBaseUrl}/`, (req, res, ctx) => {
|
||||
expect(req.url.search).toBe(
|
||||
'?type=undone&limit=10&offset=0&search=find+me',
|
||||
);
|
||||
return res(ctx.json(expectedResp));
|
||||
}),
|
||||
);
|
||||
const response = await client.getNotifications({
|
||||
type: 'undone',
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
search: 'find me',
|
||||
});
|
||||
expect(response).toEqual(expectedResp);
|
||||
});
|
||||
|
||||
it('should fetch status from correct endpoint', async () => {
|
||||
server.use(
|
||||
rest.get(`${mockBaseUrl}/status`, (_, res, ctx) =>
|
||||
res(ctx.json({ read: 1, unread: 1 })),
|
||||
),
|
||||
);
|
||||
const response = await client.getStatus();
|
||||
expect(response).toEqual({ read: 1, unread: 1 });
|
||||
});
|
||||
|
||||
it('should update notifications', async () => {
|
||||
server.use(
|
||||
rest.post(`${mockBaseUrl}/update`, async (req, res, ctx) => {
|
||||
expect(await req.json()).toEqual({
|
||||
ids: ['acdaa8ca-262b-43c1-b74b-de06e5f3b3c7'],
|
||||
done: true,
|
||||
});
|
||||
return res(ctx.json(expectedResp));
|
||||
}),
|
||||
);
|
||||
const response = await client.updateNotifications({
|
||||
ids: ['acdaa8ca-262b-43c1-b74b-de06e5f3b3c7'],
|
||||
done: true,
|
||||
});
|
||||
expect(response).toEqual(expectedResp);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -45,17 +45,17 @@ export class NotificationsClient implements NotificationsApi {
|
||||
if (options?.type) {
|
||||
queryString.append('type', options.type);
|
||||
}
|
||||
if (options?.limit) {
|
||||
if (options?.limit !== undefined) {
|
||||
queryString.append('limit', options.limit.toString(10));
|
||||
}
|
||||
if (options?.offset) {
|
||||
if (options?.offset !== undefined) {
|
||||
queryString.append('offset', options.offset.toString(10));
|
||||
}
|
||||
if (options?.search) {
|
||||
queryString.append('search', options.search);
|
||||
}
|
||||
|
||||
const urlSegment = `notifications?${queryString}`;
|
||||
const urlSegment = `?${queryString}`;
|
||||
|
||||
return await this.request<Notification[]>(urlSegment);
|
||||
}
|
||||
|
||||
@@ -7830,7 +7830,9 @@ __metadata:
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/plugin-notifications-common": "workspace:^"
|
||||
"@backstage/plugin-signals-node": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
knex: ^3.0.0
|
||||
msw: ^1.0.0
|
||||
uuid: ^8.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
Reference in New Issue
Block a user