0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

chore(test): update connector integration tests (#3765)

This commit is contained in:
Darcy Ye 2023-05-06 18:47:48 +08:00 committed by GitHub
parent 0e46ddacca
commit 4d475bfa3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 63 deletions

View file

@ -160,8 +160,8 @@ export const mockEmailConnectorConfig = {
],
};
export const mockStandardEmailConnectorId = 'mock-email-service-alternative';
export const mockStandardEmailConnectorConfig = {
export const mockAlternativeEmailConnectorId = 'mock-email-service-alternative';
export const mockAlternativeEmailConnectorConfig = {
apiKey: 'api-key-value',
fromEmail: 'noreply@logto.test.io',
fromName: 'from-name-value',
@ -205,3 +205,7 @@ export const mockSocialConnectorConfig = {
clientId: 'client_id_value',
clientSecret: 'client_secret_value',
};
export const mockSocialConnectorNewConfig = {
clientId: 'client_id_value_new',
clientSecret: 'client_secret_value_new',
};

View file

@ -7,19 +7,26 @@ import type {
import { authedAdminApi } from './api.js';
/**
* We are using `id` and `connectorFactoryId` here:
*
* - `id` is used to identify connectors from the database.
* - `connectorFactoryId` is used to identify connectors - more specifically, connector factories - in packages/connectors
* that contain metadata (considered connectors' FIXED properties) and code implementation (which determines how connectors work).
*/
export const listConnectors = async () =>
authedAdminApi.get('connectors').json<ConnectorResponse[]>();
export const getConnector = async (connectorId: string) =>
authedAdminApi.get(`connectors/${connectorId}`).json<ConnectorResponse>();
export const getConnector = async (id: string) =>
authedAdminApi.get(`connectors/${id}`).json<ConnectorResponse>();
export const listConnectorFactories = async () =>
authedAdminApi.get('connector-factories').json<ConnectorFactoryResponse[]>();
export const getConnectorFactory = async (connectorId: string) =>
authedAdminApi.get(`connector-factories/${connectorId}`).json<ConnectorFactoryResponse>();
export const getConnectorFactory = async (connectorFactoryId: string) =>
authedAdminApi.get(`connector-factories/${connectorFactoryId}`).json<ConnectorFactoryResponse>();
// FIXME @Darcy: correct use of `id` and `connectorId`.
export const postConnector = async (
payload: Pick<CreateConnector, 'connectorId' | 'config' | 'metadata' | 'syncProfile'>
) =>
@ -34,36 +41,36 @@ export const deleteConnectorById = async (id: string) =>
authedAdminApi.delete({ url: `connectors/${id}` }).json();
export const updateConnectorConfig = async (
connectorId: string,
id: string,
config: Record<string, unknown>,
metadata?: Record<string, unknown>
) =>
authedAdminApi
.patch({
url: `connectors/${connectorId}`,
url: `connectors/${id}`,
json: { config, metadata },
})
.json<ConnectorResponse>();
export const sendSmsTestMessage = async (
connectorId: string,
connectorFactoryId: string,
phone: string,
config: Record<string, unknown>
) => sendTestMessage(connectorId, 'phone', phone, config);
) => sendTestMessage(connectorFactoryId, 'phone', phone, config);
export const sendEmailTestMessage = async (
connectorId: string,
connectorFactoryId: string,
email: string,
config: Record<string, unknown>
) => sendTestMessage(connectorId, 'email', email, config);
) => sendTestMessage(connectorFactoryId, 'email', email, config);
const sendTestMessage = async (
connectorId: string,
connectorFactoryId: string,
receiverType: 'phone' | 'email',
receiver: string,
config: Record<string, unknown>
) =>
authedAdminApi.post({
url: `connectors/${connectorId}/test`,
url: `connectors/${connectorFactoryId}/test`,
json: { [receiverType]: receiver, config },
});

View file

@ -16,7 +16,7 @@ export const clearConnectorsByTypes = async (types: ConnectorType[]) => {
await Promise.all(targetConnectors.map(async (connector) => deleteConnectorById(connector.id)));
};
export const clearConnectorById = async (connectorId: string) => deleteConnectorById(connectorId);
export const clearConnectorById = async (id: string) => deleteConnectorById(id);
export const setEmailConnector = async () =>
postConnector({

View file

@ -12,7 +12,6 @@ import {
updateUserPassword,
deleteUserIdentity,
postConnector,
updateConnectorConfig,
deleteConnectorById,
} from '#src/api/index.js';
import { createResponseWithCode } from '#src/helpers/admin-tenant.js';
@ -95,9 +94,10 @@ describe('admin console user management', () => {
});
it('should delete user identities successfully', async () => {
// @darcy FIXME: merge post and update
const { id } = await postConnector({ connectorId: mockSocialConnectorId });
await updateConnectorConfig(id, mockSocialConnectorConfig);
const { id } = await postConnector({
connectorId: mockSocialConnectorId,
config: mockSocialConnectorConfig,
});
const createdUserId = await createNewSocialUserWithUsernameAndPassword(id);

View file

@ -6,9 +6,10 @@ import {
mockSmsConnectorConfig,
mockSmsConnectorId,
mockSocialConnectorConfig,
mockSocialConnectorNewConfig,
mockSocialConnectorId,
mockStandardEmailConnectorConfig,
mockStandardEmailConnectorId,
mockAlternativeEmailConnectorConfig,
mockAlternativeEmailConnectorId,
} from '#src/__mocks__/connectors-mock.js';
import {
deleteConnectorById,
@ -70,11 +71,8 @@ test('connector set-up flow', async () => {
*/
await Promise.all(
mockConnectorSetups.map(async ({ connectorId, config }) => {
// @darcy FIXME: should call post method directly
const { id } = await postConnector({ connectorId });
const { id } = await postConnector({ connectorId, config });
connectorIdMap.set(connectorId, id);
const updatedConnector = await updateConnectorConfig(id, config);
expect(updatedConnector.config).toEqual(config);
// The result of getting a connector should be same as the result of updating a connector above.
const connector = await getConnector(id);
@ -93,53 +91,64 @@ test('connector set-up flow', async () => {
// we check: the mock connector config should stay the same.
const mockSocialConnector = await getConnector(connectorIdMap.get(mockSocialConnectorId)!);
expect(mockSocialConnector.config).toEqual(mockSocialConnectorConfig);
const { config: updatedConfig } = await updateConnectorConfig(
connectorIdMap.get(mockSocialConnectorId)!,
mockSocialConnectorNewConfig
);
expect(updatedConfig).toEqual(mockSocialConnectorNewConfig);
/*
* Change to another SMS/Email connector
*/
const { id } = await postConnector({
connectorId: mockStandardEmailConnectorId,
connectorId: mockAlternativeEmailConnectorId,
config: mockAlternativeEmailConnectorConfig,
});
await updateConnectorConfig(id, mockStandardEmailConnectorConfig);
connectorIdMap.set(mockStandardEmailConnectorId, id);
connectorIdMap.set(mockAlternativeEmailConnectorId, id);
const currentConnectors = await listConnectors();
// Only one SMS/email connector should be added at a time, hence previous SMS/email connector will be deleted automatically.
expect(
currentConnectors.some((connector) => connector.connectorId === mockEmailConnectorId)
).toBeFalsy();
connectorIdMap.delete(mockEmailConnectorId);
expect(
currentConnectors.some((connector) => connector.connectorId === mockStandardEmailConnectorId)
).toBeTruthy();
expect(
currentConnectors.find((connector) => connector.connectorId === mockStandardEmailConnectorId)
currentConnectors.find((connector) => connector.connectorId === mockAlternativeEmailConnectorId)
?.config
).toEqual(mockStandardEmailConnectorConfig);
).toEqual(mockAlternativeEmailConnectorConfig);
/*
* Delete (i.e. disable) a connector
*/
await expect(
deleteConnectorById(connectorIdMap.get(mockStandardEmailConnectorId)!)
deleteConnectorById(connectorIdMap.get(mockAlternativeEmailConnectorId)!)
).resolves.not.toThrow();
connectorIdMap.delete(mockStandardEmailConnectorId);
await expect(getConnector(connectorIdMap.get(mockAlternativeEmailConnectorId)!)).rejects.toThrow(
HTTPError
);
connectorIdMap.delete(mockAlternativeEmailConnectorId);
/**
* List connectors after manually setting up connectors.
* The result of listing connectors should be same as the result of updating connectors above.
*/
expect(await listConnectors()).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: connectorIdMap.get(mockSmsConnectorId),
connectorId: mockSmsConnectorId,
config: mockSmsConnectorConfig,
}),
expect.objectContaining({
id: connectorIdMap.get(mockSocialConnectorId),
connectorId: mockSocialConnectorId,
config: mockSocialConnectorConfig,
}),
])
const allConnectorsAfterSetup = await listConnectors();
expect(
allConnectorsAfterSetup.find(({ id }) => id === connectorIdMap.get(mockSmsConnectorId))
).toEqual(
expect.objectContaining({
id: connectorIdMap.get(mockSmsConnectorId),
connectorId: mockSmsConnectorId,
config: mockSmsConnectorConfig,
})
);
expect(
allConnectorsAfterSetup.find(({ id }) => id === connectorIdMap.get(mockSocialConnectorId))
).toEqual(
expect.objectContaining({
id: connectorIdMap.get(mockSocialConnectorId),
connectorId: mockSocialConnectorId,
config: mockSocialConnectorNewConfig,
})
);
});
@ -174,14 +183,20 @@ test('override metadata for non-standard social connector', async () => {
});
test('send SMS/email test message', async () => {
const connectors = await listConnectors();
await Promise.all(
connectors.map(async ({ id }) => {
await deleteConnectorById(id);
})
);
connectorIdMap.clear();
await cleanUpConnectorTable();
const generatePhone = '8612345678901';
const generateEmail = 'test@example.com';
// Can send test message without enabling passwordless connectors (test whether `config` works).
await expect(
sendSmsTestMessage(mockSmsConnectorId, generatePhone, mockSmsConnectorConfig)
).resolves.not.toThrow();
await expect(
sendEmailTestMessage(mockEmailConnectorId, generateEmail, mockEmailConnectorConfig)
).resolves.not.toThrow();
// Set up passwordless connectors.
await Promise.all(
[{ connectorId: mockSmsConnectorId }, { connectorId: mockEmailConnectorId }].map(
async ({ connectorId }) => {
@ -191,17 +206,19 @@ test('send SMS/email test message', async () => {
)
);
const phone = '8612345678901';
const email = 'test@example.com';
await expect(
sendSmsTestMessage(mockSmsConnectorId, phone, mockSmsConnectorConfig)
sendSmsTestMessage(mockSmsConnectorId, generatePhone, mockSmsConnectorConfig)
).resolves.not.toThrow();
await expect(
sendEmailTestMessage(mockEmailConnectorId, email, mockEmailConnectorConfig)
sendEmailTestMessage(mockEmailConnectorId, generateEmail, mockEmailConnectorConfig)
).resolves.not.toThrow();
await expect(sendSmsTestMessage(mockSmsConnectorId, phone, {})).rejects.toThrow(HTTPError);
await expect(sendEmailTestMessage(mockEmailConnectorId, email, {})).rejects.toThrow(HTTPError);
// Throws due to invalid `config`s.
await expect(sendSmsTestMessage(mockSmsConnectorId, generatePhone, {})).rejects.toThrow(
HTTPError
);
await expect(sendEmailTestMessage(mockEmailConnectorId, generateEmail, {})).rejects.toThrow(
HTTPError
);
for (const [_connectorId, id] of connectorIdMap.entries()) {
// eslint-disable-next-line no-await-in-loop