mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
test(core): add api response guard and error case tests to connector api (#3806)
This commit is contained in:
parent
260f39f72d
commit
5875d4cb3b
3 changed files with 79 additions and 22 deletions
|
@ -1,6 +1,11 @@
|
|||
import { buildRawConnector } from '@logto/cli/lib/connector/index.js';
|
||||
import { demoConnectorIds, validateConfig } from '@logto/connector-kit';
|
||||
import { connectorFactoryResponseGuard, Connectors, ConnectorType } from '@logto/schemas';
|
||||
import {
|
||||
connectorFactoryResponseGuard,
|
||||
Connectors,
|
||||
ConnectorType,
|
||||
connectorResponseGuard,
|
||||
} from '@logto/schemas';
|
||||
import { buildIdGenerator } from '@logto/shared';
|
||||
import cleanDeep from 'clean-deep';
|
||||
import { string, object } from 'zod';
|
||||
|
@ -48,6 +53,8 @@ export default function connectorRoutes<T extends AuthedRouter>(
|
|||
syncProfile: true,
|
||||
})
|
||||
.merge(Connectors.createGuard.pick({ id: true }).partial()), // `id` is optional
|
||||
response: connectorResponseGuard,
|
||||
status: [200, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
|
@ -156,6 +163,8 @@ export default function connectorRoutes<T extends AuthedRouter>(
|
|||
query: object({
|
||||
target: string().optional(),
|
||||
}),
|
||||
response: connectorResponseGuard.array(),
|
||||
status: [200, 400],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { target: filterTarget } = ctx.query;
|
||||
|
@ -184,7 +193,11 @@ export default function connectorRoutes<T extends AuthedRouter>(
|
|||
|
||||
router.get(
|
||||
'/connectors/:id',
|
||||
koaGuard({ params: object({ id: string().min(1) }) }),
|
||||
koaGuard({
|
||||
params: object({ id: string().min(1) }),
|
||||
response: connectorResponseGuard,
|
||||
status: [200, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { id },
|
||||
|
@ -221,7 +234,7 @@ export default function connectorRoutes<T extends AuthedRouter>(
|
|||
koaGuard({
|
||||
params: object({ id: string().min(1) }),
|
||||
response: connectorFactoryResponseGuard,
|
||||
status: [200],
|
||||
status: [200, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
|
@ -251,6 +264,8 @@ export default function connectorRoutes<T extends AuthedRouter>(
|
|||
body: Connectors.createGuard
|
||||
.pick({ config: true, metadata: true, syncProfile: true })
|
||||
.partial(),
|
||||
response: connectorResponseGuard,
|
||||
status: [200, 400, 404, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
|
@ -300,7 +315,7 @@ export default function connectorRoutes<T extends AuthedRouter>(
|
|||
|
||||
router.delete(
|
||||
'/connectors/:id',
|
||||
koaGuard({ params: object({ id: string().min(1) }) }),
|
||||
koaGuard({ params: object({ id: string().min(1) }), status: [204, 404] }),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { id },
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
listConnectorFactories,
|
||||
getConnectorFactory,
|
||||
} from '#src/api/connector.js';
|
||||
import { createResponseWithCode } from '#src/helpers/admin-tenant.js';
|
||||
|
||||
const connectorIdMap = new Map<string, string>();
|
||||
|
||||
|
@ -30,6 +31,16 @@ const mockConnectorSetups = [
|
|||
{ connectorId: mockSocialConnectorId, config: mockSocialConnectorConfig },
|
||||
];
|
||||
|
||||
const cleanUpConnectorTable = async () => {
|
||||
const connectors = await listConnectors();
|
||||
await Promise.all(
|
||||
connectors.map(async ({ id }) => {
|
||||
await deleteConnectorById(id);
|
||||
})
|
||||
);
|
||||
connectorIdMap.clear();
|
||||
};
|
||||
|
||||
/*
|
||||
* We'd better only use mock connectors in integration tests.
|
||||
* Since we will refactor connectors soon, keep using some real connectors
|
||||
|
@ -52,16 +63,7 @@ test('connector set-up flow', async () => {
|
|||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Clean up `connector` table
|
||||
*/
|
||||
const connectors = await listConnectors();
|
||||
await Promise.all(
|
||||
connectors.map(async ({ id }) => {
|
||||
await deleteConnectorById(id);
|
||||
})
|
||||
);
|
||||
connectorIdMap.clear();
|
||||
await cleanUpConnectorTable();
|
||||
|
||||
/*
|
||||
* Set up social/SMS/email connectors
|
||||
|
@ -141,6 +143,36 @@ test('connector set-up flow', async () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('create connector with non-exist connectorId', async () => {
|
||||
await cleanUpConnectorTable();
|
||||
await expect(postConnector({ connectorId: 'non-exist-id' })).rejects.toMatchObject(
|
||||
createResponseWithCode(422)
|
||||
);
|
||||
});
|
||||
|
||||
test('create non standard social connector with target', async () => {
|
||||
await cleanUpConnectorTable();
|
||||
await expect(
|
||||
postConnector({ connectorId: mockSocialConnectorId, metadata: { target: 'target' } })
|
||||
).rejects.toMatchObject(createResponseWithCode(400));
|
||||
});
|
||||
|
||||
test('create duplicated social connector', async () => {
|
||||
await cleanUpConnectorTable();
|
||||
await postConnector({ connectorId: mockSocialConnectorId });
|
||||
await expect(postConnector({ connectorId: mockSocialConnectorId })).rejects.toMatchObject(
|
||||
createResponseWithCode(422)
|
||||
);
|
||||
});
|
||||
|
||||
test('override metadata for non-standard social connector', async () => {
|
||||
await cleanUpConnectorTable();
|
||||
const { id } = await postConnector({ connectorId: mockSocialConnectorId });
|
||||
await expect(updateConnectorConfig(id, {}, { target: 'target' })).rejects.toMatchObject(
|
||||
createResponseWithCode(400)
|
||||
);
|
||||
});
|
||||
|
||||
test('send SMS/email test message', async () => {
|
||||
const connectors = await listConnectors();
|
||||
await Promise.all(
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
import type { BaseConnector, ConnectorMetadata } from '@logto/connector-kit';
|
||||
import { ConnectorType, connectorMetadataGuard } from '@logto/connector-kit';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { Connector } from '../db-entries/index.js';
|
||||
import { Connectors } from '../db-entries/index.js';
|
||||
|
||||
export type { ConnectorMetadata } from '@logto/connector-kit';
|
||||
export { ConnectorType, ConnectorPlatform } from '@logto/connector-kit';
|
||||
|
||||
export type ConnectorResponse = Pick<
|
||||
Connector,
|
||||
'id' | 'syncProfile' | 'config' | 'metadata' | 'connectorId'
|
||||
> &
|
||||
Omit<BaseConnector<ConnectorType>, 'configGuard' | 'metadata'> &
|
||||
ConnectorMetadata & { isDemo?: boolean };
|
||||
export const connectorResponseGuard = Connectors.guard
|
||||
.pick({
|
||||
id: true,
|
||||
syncProfile: true,
|
||||
config: true,
|
||||
metadata: true,
|
||||
connectorId: true,
|
||||
})
|
||||
.merge(connectorMetadataGuard)
|
||||
.merge(
|
||||
z.object({
|
||||
type: z.nativeEnum(ConnectorType),
|
||||
isDemo: z.boolean().optional(),
|
||||
})
|
||||
);
|
||||
|
||||
export type ConnectorResponse = z.infer<typeof connectorResponseGuard>;
|
||||
|
||||
export const connectorFactoryResponseGuard = z
|
||||
.object({
|
||||
|
|
Loading…
Reference in a new issue