0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-04-07 23:01:25 -05:00

test(core): add UTs for connector route ()

This commit is contained in:
Darcy Ye 2022-03-10 11:54:33 +08:00 committed by GitHub
parent 012db5e07d
commit 67aad46a2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 613 additions and 0 deletions
packages/core/src

View file

@ -0,0 +1,455 @@
import { Connector, ConnectorType } from '@logto/schemas';
import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
ValidateConfig,
} from '@/connectors/types';
import RequestError from '@/errors/RequestError';
import { updateConnector } from '@/queries/connector';
import assertThat from '@/utils/assert-that';
import { mockConnectorList, mockConnectorInstanceList } from '@/utils/mock';
import { createRequester } from '@/utils/test-utils';
import connectorRoutes from './connector';
type ConnectorInstance = {
connector: Connector;
metadata: ConnectorMetadata;
validateConfig?: ValidateConfig;
};
const findConnectorByIdPlaceHolder = jest.fn() as jest.MockedFunction<
(connectorId: string) => Promise<Connector>
>;
const getConnectorInstanceByIdPlaceHolder = jest.fn() as jest.MockedFunction<
(connectorId: string) => Promise<ConnectorInstance>
>;
const getConnectorInstancesPlaceHolder = jest.fn() as jest.MockedFunction<
() => Promise<ConnectorInstance[]>
>;
jest.mock('@/queries/connector', () => ({
findConnectorById: async (connectorId: string) => findConnectorByIdPlaceHolder(connectorId),
findAllConnectors: jest.fn(),
updateConnector: jest.fn(),
}));
jest.mock('@/connectors', () => ({
getConnectorInstanceById: async (connectorId: string) =>
getConnectorInstanceByIdPlaceHolder(connectorId),
getConnectorInstances: async () => getConnectorInstancesPlaceHolder(),
}));
describe('connector route', () => {
const connectorRequest = createRequester({ authedRoutes: connectorRoutes });
describe('GET /connectors', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('throws if more than one email connector is enabled', async () => {
getConnectorInstancesPlaceHolder.mockResolvedValue(mockConnectorInstanceList);
const response = await connectorRequest.get('/connectors').send({});
expect(response).toHaveProperty('statusCode', 400);
});
it('throws if more than one SMS connector is enabled', async () => {
getConnectorInstancesPlaceHolder.mockResolvedValue(
mockConnectorInstanceList.filter(
(connecotrInstance) => connecotrInstance.metadata.type !== ConnectorType.Email
)
);
const response = await connectorRequest.get('/connectors').send({});
expect(response).toHaveProperty('statusCode', 400);
});
it('shows all connectors', async () => {
getConnectorInstancesPlaceHolder.mockResolvedValue(
mockConnectorInstanceList.filter(
(connecotrInstance) => connecotrInstance.metadata.type === ConnectorType.Social
)
);
const response = await connectorRequest.get('/connectors').send({});
expect(response).toHaveProperty('statusCode', 200);
});
});
describe('GET /connectors/:id', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('throws when connector can not be found by given connectorId (locally)', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
const found = mockConnectorInstanceList.find(
(connectorInstance) => connectorInstance.metadata.id === 'connector'
);
assertThat(found, new RequestError({ code: 'entity.not_found', status: 404 }));
return found;
});
const response = await connectorRequest.get('/connectors/findConnector').send({});
expect(response).toHaveProperty('statusCode', 404);
});
it('throws when connector can not be found by given connectorId (remotely)', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (id: string) => {
const foundConnectorInstance = mockConnectorInstanceList.find(
(connectorInstance) => connectorInstance.metadata.id === id
);
assertThat(
foundConnectorInstance,
new RequestError({ code: 'entity.not_found', status: 404 })
);
const foundConnector = mockConnectorList.find((connector) => connector.id === 'connector0');
assertThat(foundConnector, 'entity.not_found');
return { foundConnector, ...foundConnectorInstance };
});
const response = await connectorRequest.get('/connectors/connector_0').send({});
expect(response).toHaveProperty('statusCode', 400);
});
it('shows found connector information', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (id: string) => {
const foundConnectorInstance = mockConnectorInstanceList.find(
(connectorInstance) => connectorInstance.metadata.id === id
);
assertThat(
foundConnectorInstance,
new RequestError({ code: 'entity.not_found', status: 404 })
);
const foundConnector = mockConnectorList.find((connector) => connector.id === id);
assertThat(foundConnector, 'entity.not_found');
return { foundConnector, ...foundConnectorInstance };
});
const response = await connectorRequest.get('/connectors/connector_0').send({});
expect(response).toHaveProperty('statusCode', 200);
});
});
describe('PATCH /connectors/:id/enabled', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('throws if connector can not be found (locally)', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
const found = mockConnectorInstanceList.find(
(connectorInstance) => connectorInstance.metadata.id === 'connector'
);
assertThat(found, new RequestError({ code: 'entity.not_found', status: 404 }));
return found;
});
const response = await connectorRequest
.patch('/connectors/findConnector/enabled')
.send({ enabled: true });
expect(response).toHaveProperty('statusCode', 404);
});
it('throws if connector can not be found (remotely)', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (id: string) => {
const foundConnectorInstance = mockConnectorInstanceList.find(
(connectorInstance) => connectorInstance.metadata.id === id
);
assertThat(
foundConnectorInstance,
new RequestError({ code: 'entity.not_found', status: 404 })
);
const foundConnector = mockConnectorList.find((connector) => connector.id === 'connector0');
assertThat(foundConnector, 'entity.not_found');
return { foundConnector, ...foundConnectorInstance };
});
const response = await connectorRequest
.patch('/connectors/connector_0/enabled')
.send({ enabled: true });
expect(response).toHaveProperty('statusCode', 400);
});
it('enables one of the social connectors', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
return {
connector: {
id: 'connector_0',
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
},
metadata: {
id: 'connector_0',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
};
});
const response = await connectorRequest
.patch('/connectors/connector_0/enabled')
.send({ enabled: true });
expect(updateConnector).toHaveBeenCalledWith(
expect.objectContaining({
where: { id: 'connector_0' },
set: { enabled: true },
})
);
expect(response.body).toMatchObject({
metadata: {
id: 'connector_0',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
});
expect(response).toHaveProperty('statusCode', 200);
});
it('disables one of the social connectors', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
return {
connector: {
id: 'connector_0',
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
},
metadata: {
id: 'connector_0',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
};
});
const response = await connectorRequest
.patch('/connectors/connector_0/enabled')
.send({ enabled: false });
expect(updateConnector).toHaveBeenCalledWith(
expect.objectContaining({
where: { id: 'connector_0' },
set: { enabled: false },
})
);
expect(response.body).toMatchObject({
metadata: {
id: 'connector_0',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
});
expect(response).toHaveProperty('statusCode', 200);
});
it('enables one of the email/sms connectors', async () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(mockConnectorInstanceList);
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
return {
connector: {
id: 'connector_1',
enabled: true,
config: {},
createdAt: 1_234_567_890_234,
},
metadata: {
id: 'connector_1',
type: ConnectorType.SMS,
name: {},
logo: './logo.png',
description: {},
},
};
});
const response = await connectorRequest
.patch('/connectors/connector_1/enabled')
.send({ enabled: true });
expect(updateConnector).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
where: { id: 'connector_1' },
set: { enabled: false },
})
);
expect(updateConnector).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
where: { id: 'connector_5' },
set: { enabled: false },
})
);
expect(updateConnector).toHaveBeenNthCalledWith(
3,
expect.objectContaining({
where: { id: 'connector_1' },
set: { enabled: true },
})
);
expect(response.body).toMatchObject({
metadata: {
id: 'connector_1',
type: ConnectorType.SMS,
name: {},
logo: './logo.png',
description: {},
},
});
expect(response).toHaveProperty('statusCode', 200);
});
it('disables one of the email/sms connectors', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
return {
connector: {
id: 'connector_4',
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
metadata: {
id: 'connector_4',
type: ConnectorType.Email,
name: {},
logo: './logo.png',
description: {},
},
};
});
const response = await connectorRequest
.patch('/connectors/connector_4/enabled')
.send({ enabled: false });
expect(updateConnector).toHaveBeenCalledWith(
expect.objectContaining({
where: { id: 'connector_4' },
set: { enabled: false },
})
);
expect(response.body).toMatchObject({
metadata: {
id: 'connector_4',
type: ConnectorType.Email,
name: {},
logo: './logo.png',
description: {},
},
});
expect(response).toHaveProperty('statusCode', 200);
});
});
describe('PATCH /connectors/:id', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('throws when connector can not be found by given connectorId (locally)', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
const found = mockConnectorInstanceList.find(
(connectorInstance) => connectorInstance.metadata.id === 'connector'
);
assertThat(found, new RequestError({ code: 'entity.not_found', status: 404 }));
return found;
});
const response = await connectorRequest.patch('/connectors/findConnector').send({});
expect(response).toHaveProperty('statusCode', 404);
});
it('throws when connector can not be found by given connectorId (remotely)', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (id: string) => {
const foundConnectorInstance = mockConnectorInstanceList.find(
(connectorInstance) => connectorInstance.metadata.id === id
);
assertThat(
foundConnectorInstance,
new RequestError({ code: 'entity.not_found', status: 404 })
);
const foundConnector = mockConnectorList.find((connector) => connector.id === 'connector0');
assertThat(foundConnector, 'entity.not_found');
return { foundConnector, ...foundConnectorInstance };
});
const response = await connectorRequest.patch('/connectors/connector_0').send({});
expect(response).toHaveProperty('statusCode', 400);
});
it('config validation fails', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
return {
connector: {
id: 'connector_0',
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
},
metadata: {
id: 'connector_0',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
validateConfig: async (_config) => {
throw new ConnectorError(ConnectorErrorCodes.InvalidConfig);
},
};
});
const response = await connectorRequest
.patch('/connectors/connector_0')
.send({ config: { cliend_id: 'client_id', client_secret: 'client_secret' } });
expect(response).toHaveProperty('statusCode', 500);
});
it('successfully updates connector configs', async () => {
getConnectorInstanceByIdPlaceHolder.mockImplementationOnce(async (_id: string) => {
return {
connector: {
id: 'connector_0',
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
},
metadata: {
id: 'connector_0',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
// eslint-disable-next-line @typescript-eslint/no-empty-function
validateConfig: async (_config) => {},
};
});
const response = await connectorRequest
.patch('/connectors/connector_0')
.send({ config: { cliend_id: 'client_id', client_secret: 'client_secret' } });
expect(updateConnector).toHaveBeenCalledWith(
expect.objectContaining({
where: { id: 'connector_0' },
set: { config: { cliend_id: 'client_id', client_secret: 'client_secret' } },
})
);
expect(response.body).toMatchObject({
metadata: {
id: 'connector_0',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
});
expect(response).toHaveProperty('statusCode', 200);
});
});
});

View file

@ -11,11 +11,13 @@ import {
BrandingStyle,
Language,
Connector,
ConnectorMetadata,
Passcode,
PasscodeType,
UserLog,
UserLogType,
UserLogResult,
ConnectorType,
} from '@logto/schemas';
import pick from 'lodash.pick';
@ -188,6 +190,162 @@ export const mockConnector: Connector = {
createdAt: 1_645_334_775_356,
};
export const mockConnectorList: Connector[] = [
{
id: 'connector_0',
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
},
{
id: 'connector_1',
enabled: true,
config: {},
createdAt: 1_234_567_890_234,
},
{
id: 'connector_2',
enabled: true,
config: {},
createdAt: 1_234_567_890_345,
},
{
id: 'connector_3',
enabled: true,
config: {},
createdAt: 1_234_567_890_456,
},
{
id: 'connector_4',
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
{
id: 'connector_5',
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
{
id: 'connector_6',
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
];
export const mockConnectorInstanceList: Array<{
connector: Connector;
metadata: ConnectorMetadata;
}> = [
{
connector: {
id: 'connector_0',
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
},
metadata: {
id: 'connector_0',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
},
{
connector: {
id: 'connector_1',
enabled: true,
config: {},
createdAt: 1_234_567_890_234,
},
metadata: {
id: 'connector_1',
type: ConnectorType.SMS,
name: {},
logo: './logo.png',
description: {},
},
},
{
connector: {
id: 'connector_2',
enabled: true,
config: {},
createdAt: 1_234_567_890_345,
},
metadata: {
id: 'connector_2',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
},
{
connector: {
id: 'connector_3',
enabled: true,
config: {},
createdAt: 1_234_567_890_456,
},
metadata: {
id: 'connector_3',
type: ConnectorType.Social,
name: {},
logo: './logo.png',
description: {},
},
},
{
connector: {
id: 'connector_4',
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
metadata: {
id: 'connector_4',
type: ConnectorType.Email,
name: {},
logo: './logo.png',
description: {},
},
},
{
connector: {
id: 'connector_5',
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
metadata: {
id: 'connector_5',
type: ConnectorType.SMS,
name: {},
logo: './logo.png',
description: {},
},
},
{
connector: {
id: 'connector_6',
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
metadata: {
id: 'connector_6',
type: ConnectorType.Email,
name: {},
logo: './logo.png',
description: {},
},
},
];
export const mockPasscode: Passcode = {
id: 'foo',
interactionJti: 'jti',