2022-12-26 19:59:33 +08:00
|
|
|
import { InteractionEvent, LogResult } from '@logto/schemas';
|
2022-12-22 17:15:50 +08:00
|
|
|
import { HookEvent } from '@logto/schemas/lib/models/hooks.js';
|
2022-12-26 20:12:19 +08:00
|
|
|
import { createMockUtils } from '@logto/shared/esm';
|
2022-12-22 17:15:50 +08:00
|
|
|
import type { InferModelType } from '@withtyped/server';
|
|
|
|
import { got } from 'got';
|
|
|
|
|
2023-01-10 13:38:12 +08:00
|
|
|
import type { ModelRouters } from '#src/model-routers/index.js';
|
2022-12-22 17:15:50 +08:00
|
|
|
|
|
|
|
import type { Interaction } from './hook.js';
|
|
|
|
|
|
|
|
const { jest } = import.meta;
|
2023-01-10 13:38:12 +08:00
|
|
|
const { mockEsmDefault, mockEsmWithActual } = createMockUtils(jest);
|
|
|
|
|
|
|
|
const nanoIdMock = 'mockId';
|
|
|
|
await mockEsmWithActual('@logto/core-kit', () => ({
|
|
|
|
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
|
|
buildIdGenerator: () => () => nanoIdMock,
|
|
|
|
generateStandardId: () => nanoIdMock,
|
|
|
|
}));
|
|
|
|
|
|
|
|
const { createModelRouters } = await import('#src/model-routers/index.js');
|
|
|
|
const { MockQueryClient } = await import('#src/test-utils/query-client.js');
|
|
|
|
const { MockQueries } = await import('#src/test-utils/tenant.js');
|
2022-12-22 17:15:50 +08:00
|
|
|
|
|
|
|
const queryClient = new MockQueryClient();
|
|
|
|
const queryFunction = jest.fn();
|
|
|
|
|
|
|
|
const url = 'https://logto.gg';
|
2023-01-10 13:38:12 +08:00
|
|
|
const hook: InferModelType<ModelRouters['hook']['model']> = {
|
2022-12-22 17:15:50 +08:00
|
|
|
id: 'foo',
|
|
|
|
event: HookEvent.PostSignIn,
|
|
|
|
config: { headers: { bar: 'baz' }, url, retries: 3 },
|
|
|
|
createdAt: new Date(),
|
|
|
|
};
|
|
|
|
|
2022-12-26 19:59:33 +08:00
|
|
|
const post = jest
|
|
|
|
.spyOn(got, 'post')
|
|
|
|
// @ts-expect-error for testing
|
|
|
|
.mockImplementation(jest.fn(async () => ({ statusCode: 200, body: '{"message":"ok"}' })));
|
|
|
|
|
2023-01-10 13:38:12 +08:00
|
|
|
const insertLog = jest.fn();
|
2022-12-23 19:02:30 +08:00
|
|
|
|
2022-12-22 17:15:50 +08:00
|
|
|
// eslint-disable-next-line unicorn/consistent-function-scoping
|
2023-01-07 17:22:15 +08:00
|
|
|
mockEsmDefault('#src/env-set/create-query-client.js', () => () => queryClient);
|
2022-12-22 17:15:50 +08:00
|
|
|
jest.spyOn(queryClient, 'query').mockImplementation(queryFunction);
|
|
|
|
|
2023-01-10 13:38:12 +08:00
|
|
|
const { createHookLibrary } = await import('./hook.js');
|
|
|
|
const modelRouters = createModelRouters(new MockQueryClient());
|
|
|
|
const { triggerInteractionHooksIfNeeded } = createHookLibrary(
|
|
|
|
new MockQueries({
|
|
|
|
// @ts-expect-error
|
|
|
|
users: { findUserById: () => ({ id: 'user_id', username: 'user', extraField: 'not_ok' }) },
|
|
|
|
applications: {
|
|
|
|
// @ts-expect-error
|
|
|
|
findApplicationById: async () => ({ id: 'app_id', extraField: 'not_ok' }),
|
|
|
|
},
|
|
|
|
logs: { insertLog },
|
|
|
|
}),
|
|
|
|
modelRouters
|
|
|
|
);
|
|
|
|
|
|
|
|
const readAll = jest
|
|
|
|
.spyOn(modelRouters.hook.client, 'readAll')
|
|
|
|
.mockResolvedValue({ rows: [hook], rowCount: 1 });
|
2022-12-22 17:15:50 +08:00
|
|
|
|
|
|
|
describe('triggerInteractionHooksIfNeeded()', () => {
|
|
|
|
afterEach(() => {
|
|
|
|
jest.clearAllMocks();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return if no user ID found', async () => {
|
2022-12-26 19:59:33 +08:00
|
|
|
await triggerInteractionHooksIfNeeded(InteractionEvent.SignIn);
|
2022-12-22 17:15:50 +08:00
|
|
|
|
|
|
|
expect(queryFunction).not.toBeCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should set correct payload when hook triggered', async () => {
|
|
|
|
jest.useFakeTimers().setSystemTime(100_000);
|
|
|
|
|
|
|
|
await triggerInteractionHooksIfNeeded(
|
2022-12-26 19:59:33 +08:00
|
|
|
InteractionEvent.SignIn,
|
2022-12-22 17:15:50 +08:00
|
|
|
// @ts-expect-error for testing
|
|
|
|
{
|
|
|
|
jti: 'some_jti',
|
2022-12-24 20:42:18 +08:00
|
|
|
result: { login: { accountId: '123' } },
|
2022-12-22 17:15:50 +08:00
|
|
|
params: { client_id: 'some_client' },
|
|
|
|
} as Interaction
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(readAll).toHaveBeenCalled();
|
|
|
|
expect(post).toHaveBeenCalledWith(url, {
|
|
|
|
headers: { 'user-agent': 'Logto (https://logto.io)', bar: 'baz' },
|
|
|
|
json: {
|
|
|
|
hookId: 'foo',
|
|
|
|
event: 'PostSignIn',
|
|
|
|
interactionEvent: 'SignIn',
|
|
|
|
sessionId: 'some_jti',
|
|
|
|
userId: '123',
|
|
|
|
user: { id: 'user_id', username: 'user' },
|
|
|
|
application: { id: 'app_id' },
|
|
|
|
createdAt: new Date(100_000).toISOString(),
|
|
|
|
},
|
|
|
|
retry: { limit: 3 },
|
|
|
|
timeout: { request: 10_000 },
|
|
|
|
});
|
2022-12-26 19:59:33 +08:00
|
|
|
|
|
|
|
const calledPayload: unknown = insertLog.mock.calls[0][0];
|
|
|
|
expect(calledPayload).toHaveProperty('id', nanoIdMock);
|
|
|
|
expect(calledPayload).toHaveProperty('key', 'TriggerHook.' + HookEvent.PostSignIn);
|
|
|
|
expect(calledPayload).toHaveProperty('payload.result', LogResult.Success);
|
|
|
|
expect(calledPayload).toHaveProperty('payload.hookId', 'foo');
|
|
|
|
expect(calledPayload).toHaveProperty('payload.json.event', HookEvent.PostSignIn);
|
|
|
|
expect(calledPayload).toHaveProperty('payload.json.interactionEvent', InteractionEvent.SignIn);
|
|
|
|
expect(calledPayload).toHaveProperty('payload.json.hookId', 'foo');
|
|
|
|
expect(calledPayload).toHaveProperty('payload.json.userId', '123');
|
|
|
|
expect(calledPayload).toHaveProperty('payload.response.statusCode', 200);
|
|
|
|
expect(calledPayload).toHaveProperty('payload.response.body.message', 'ok');
|
2022-12-22 17:15:50 +08:00
|
|
|
jest.useRealTimers();
|
|
|
|
});
|
|
|
|
});
|