2023-07-13 01:14:48 -07:00
|
|
|
import cookieParser from 'cookie-parser';
|
|
|
|
import express from 'express';
|
|
|
|
import type superagent from 'superagent';
|
|
|
|
import request from 'supertest';
|
2024-09-12 09:07:18 -07:00
|
|
|
import { Container } from 'typedi';
|
2023-07-13 01:14:48 -07:00
|
|
|
import { URL } from 'url';
|
|
|
|
|
2024-09-12 09:07:18 -07:00
|
|
|
import { AuthService } from '@/auth/auth.service';
|
2023-07-13 01:14:48 -07:00
|
|
|
import config from '@/config';
|
|
|
|
import { AUTH_COOKIE_NAME } from '@/constants';
|
2024-08-28 08:57:46 -07:00
|
|
|
import type { User } from '@/databases/entities/user';
|
2024-06-19 00:57:40 -07:00
|
|
|
import { ControllerRegistry } from '@/decorators';
|
2024-09-12 09:07:18 -07:00
|
|
|
import { License } from '@/license';
|
|
|
|
import { Logger } from '@/logger';
|
2024-02-28 04:12:28 -08:00
|
|
|
import { rawBodyReader, bodyParser } from '@/middlewares';
|
2023-07-13 01:14:48 -07:00
|
|
|
import { PostHogClient } from '@/posthog';
|
2024-01-26 04:49:39 -08:00
|
|
|
import { Push } from '@/push';
|
2024-06-19 00:57:40 -07:00
|
|
|
import type { APIRequest } from '@/requests';
|
2024-09-12 09:07:18 -07:00
|
|
|
import { Telemetry } from '@/telemetry';
|
2023-07-13 01:14:48 -07:00
|
|
|
|
2023-11-10 06:04:26 -08:00
|
|
|
import { mockInstance } from '../../../shared/mocking';
|
2024-02-28 04:12:28 -08:00
|
|
|
import { PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from '../constants';
|
2023-11-30 00:23:09 -08:00
|
|
|
import { LicenseMocker } from '../license';
|
2024-09-12 09:07:18 -07:00
|
|
|
import * as testDb from '../test-db';
|
|
|
|
import type { SetupProps, TestServer } from '../types';
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
/**
|
|
|
|
* Plugin to prefix a path segment into a request URL pathname.
|
|
|
|
*
|
|
|
|
* Example: http://127.0.0.1:62100/me/password → http://127.0.0.1:62100/rest/me/password
|
|
|
|
*/
|
|
|
|
function prefix(pathSegment: string) {
|
|
|
|
return async function (request: superagent.SuperAgentRequest) {
|
|
|
|
const url = new URL(request.url);
|
|
|
|
|
|
|
|
// enforce consistency at call sites
|
|
|
|
if (url.pathname[0] !== '/') {
|
|
|
|
throw new Error('Pathname must start with a forward slash');
|
|
|
|
}
|
|
|
|
|
|
|
|
url.pathname = pathSegment + url.pathname;
|
|
|
|
request.url = url.toString();
|
2024-01-17 07:08:50 -08:00
|
|
|
return await request;
|
2023-07-13 01:14:48 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-04-10 01:37:23 -07:00
|
|
|
const browserId = 'test-browser-id';
|
2024-09-05 02:04:48 -07:00
|
|
|
function createAgent(
|
|
|
|
app: express.Application,
|
|
|
|
options?: { auth: boolean; user?: User; noRest?: boolean },
|
|
|
|
) {
|
2023-07-13 01:14:48 -07:00
|
|
|
const agent = request.agent(app);
|
2024-09-05 02:04:48 -07:00
|
|
|
|
|
|
|
const withRestSegment = !options?.noRest;
|
|
|
|
|
|
|
|
if (withRestSegment) void agent.use(prefix(REST_PATH_SEGMENT));
|
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
if (options?.auth && options?.user) {
|
2024-04-10 01:37:23 -07:00
|
|
|
const token = Container.get(AuthService).issueJWT(options.user, browserId);
|
2023-08-03 05:03:26 -07:00
|
|
|
agent.jar.setCookie(`${AUTH_COOKIE_NAME}=${token}`);
|
2023-07-13 01:14:48 -07:00
|
|
|
}
|
|
|
|
return agent;
|
|
|
|
}
|
|
|
|
|
2024-09-26 05:58:49 -07:00
|
|
|
const userDoesNotHaveApiKey = (user: User) => {
|
|
|
|
return !user.apiKeys || !Array.from(user.apiKeys) || user.apiKeys.length === 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
const publicApiAgent = (
|
2023-07-13 01:14:48 -07:00
|
|
|
app: express.Application,
|
2024-09-26 05:58:49 -07:00
|
|
|
{ user, apiKey, version = 1 }: { user?: User; apiKey?: string; version?: number },
|
|
|
|
) => {
|
|
|
|
if (user && apiKey) {
|
|
|
|
throw new Error('Cannot provide both user and API key');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (user && userDoesNotHaveApiKey(user)) {
|
|
|
|
throw new Error('User does not have an API key');
|
|
|
|
}
|
|
|
|
|
|
|
|
const agentApiKey = apiKey ?? user?.apiKeys[0].apiKey;
|
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
const agent = request.agent(app);
|
|
|
|
void agent.use(prefix(`${PUBLIC_API_REST_PATH_SEGMENT}/v${version}`));
|
2024-09-26 05:58:49 -07:00
|
|
|
if (!user && !apiKey) return agent;
|
|
|
|
void agent.set({ 'X-N8N-API-KEY': agentApiKey });
|
2023-07-13 01:14:48 -07:00
|
|
|
return agent;
|
2024-09-26 05:58:49 -07:00
|
|
|
};
|
2023-07-13 01:14:48 -07:00
|
|
|
|
|
|
|
export const setupTestServer = ({
|
|
|
|
endpointGroups,
|
|
|
|
enabledFeatures,
|
2023-12-04 04:56:48 -08:00
|
|
|
quotas,
|
2023-07-13 01:14:48 -07:00
|
|
|
}: SetupProps): TestServer => {
|
|
|
|
const app = express();
|
2023-08-11 00:18:33 -07:00
|
|
|
app.use(rawBodyReader);
|
2023-08-01 08:32:30 -07:00
|
|
|
app.use(cookieParser());
|
2024-04-10 01:37:23 -07:00
|
|
|
app.use((req: APIRequest, _, next) => {
|
|
|
|
req.browserId = browserId;
|
|
|
|
next();
|
|
|
|
});
|
2023-08-01 08:32:30 -07:00
|
|
|
|
2023-10-25 07:35:22 -07:00
|
|
|
// Mock all telemetry and logging
|
2023-12-27 02:50:43 -08:00
|
|
|
mockInstance(Logger);
|
2023-10-25 07:35:22 -07:00
|
|
|
mockInstance(PostHogClient);
|
2024-01-26 04:49:39 -08:00
|
|
|
mockInstance(Push);
|
2024-09-12 09:07:18 -07:00
|
|
|
mockInstance(Telemetry);
|
2023-09-20 06:21:42 -07:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
const testServer: TestServer = {
|
|
|
|
app,
|
|
|
|
httpServer: app.listen(0),
|
|
|
|
authAgentFor: (user: User) => createAgent(app, { auth: true, user }),
|
|
|
|
authlessAgent: createAgent(app),
|
2024-09-05 02:04:48 -07:00
|
|
|
restlessAgent: createAgent(app, { auth: false, noRest: true }),
|
2023-07-13 01:14:48 -07:00
|
|
|
publicApiAgentFor: (user) => publicApiAgent(app, { user }),
|
2024-09-26 05:58:49 -07:00
|
|
|
publicApiAgentWithApiKey: (apiKey) => publicApiAgent(app, { apiKey }),
|
|
|
|
publicApiAgentWithoutApiKey: () => publicApiAgent(app, {}),
|
2023-11-30 00:23:09 -08:00
|
|
|
license: new LicenseMocker(),
|
2023-07-13 01:14:48 -07:00
|
|
|
};
|
|
|
|
|
2024-04-10 05:02:02 -07:00
|
|
|
// eslint-disable-next-line complexity
|
2023-07-13 01:14:48 -07:00
|
|
|
beforeAll(async () => {
|
|
|
|
await testDb.init();
|
|
|
|
|
|
|
|
config.set('userManagement.jwtSecret', 'My JWT secret');
|
|
|
|
config.set('userManagement.isInstanceOwnerSetUp', true);
|
|
|
|
|
2023-11-30 00:23:09 -08:00
|
|
|
testServer.license.mock(Container.get(License));
|
2023-07-13 01:14:48 -07:00
|
|
|
if (enabledFeatures) {
|
2023-11-30 00:23:09 -08:00
|
|
|
testServer.license.setDefaults({
|
|
|
|
features: enabledFeatures,
|
2023-12-04 04:56:48 -08:00
|
|
|
quotas,
|
2023-11-30 00:23:09 -08:00
|
|
|
});
|
2023-07-13 01:14:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!endpointGroups) return;
|
|
|
|
|
2023-08-11 00:18:33 -07:00
|
|
|
app.use(bodyParser);
|
2023-08-01 08:32:30 -07:00
|
|
|
|
2024-02-28 04:12:28 -08:00
|
|
|
const enablePublicAPI = endpointGroups?.includes('publicApi');
|
2023-11-08 07:29:39 -08:00
|
|
|
if (enablePublicAPI) {
|
2024-08-28 04:59:27 -07:00
|
|
|
const { loadPublicApiVersions } = await import('@/public-api');
|
2023-11-08 07:29:39 -08:00
|
|
|
const { apiRouters } = await loadPublicApiVersions(PUBLIC_API_REST_PATH_SEGMENT);
|
|
|
|
app.use(...apiRouters);
|
|
|
|
}
|
2023-07-13 01:14:48 -07:00
|
|
|
|
2024-09-05 02:04:48 -07:00
|
|
|
if (endpointGroups?.includes('health')) {
|
|
|
|
app.get('/healthz/readiness', async (_req, res) => {
|
|
|
|
testDb.isReady()
|
|
|
|
? res.status(200).send({ status: 'ok' })
|
|
|
|
: res.status(503).send({ status: 'error' });
|
|
|
|
});
|
|
|
|
}
|
2023-11-08 07:29:39 -08:00
|
|
|
if (endpointGroups.length) {
|
|
|
|
for (const group of endpointGroups) {
|
|
|
|
switch (group) {
|
2024-09-02 06:20:08 -07:00
|
|
|
case 'annotationTags':
|
2024-09-26 04:15:08 -07:00
|
|
|
await import('@/controllers/annotation-tags.controller.ee');
|
2024-09-02 06:20:08 -07:00
|
|
|
break;
|
|
|
|
|
2023-11-08 07:29:39 -08:00
|
|
|
case 'credentials':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/credentials/credentials.controller');
|
2023-11-08 07:29:39 -08:00
|
|
|
break;
|
2023-07-13 01:14:48 -07:00
|
|
|
|
2023-11-08 07:29:39 -08:00
|
|
|
case 'workflows':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/workflows/workflows.controller');
|
2023-11-08 07:29:39 -08:00
|
|
|
break;
|
2023-07-13 01:14:48 -07:00
|
|
|
|
2023-11-08 07:29:39 -08:00
|
|
|
case 'executions':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/executions/executions.controller');
|
2023-11-08 07:29:39 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'variables':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/environments/variables/variables.controller.ee');
|
2023-11-08 07:29:39 -08:00
|
|
|
break;
|
2023-07-13 01:14:48 -07:00
|
|
|
|
2023-11-08 07:29:39 -08:00
|
|
|
case 'license':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/license/license.controller');
|
2023-11-08 07:29:39 -08:00
|
|
|
break;
|
2023-07-13 01:14:48 -07:00
|
|
|
|
2023-08-04 11:51:07 -07:00
|
|
|
case 'metrics':
|
2024-07-15 03:16:27 -07:00
|
|
|
const { PrometheusMetricsService } = await import(
|
|
|
|
'@/metrics/prometheus-metrics.service'
|
|
|
|
);
|
2024-07-18 01:27:35 -07:00
|
|
|
await Container.get(PrometheusMetricsService).init(app);
|
2023-08-04 11:51:07 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'eventBus':
|
2024-08-28 08:57:46 -07:00
|
|
|
await import('@/eventbus/event-bus.controller');
|
2023-07-13 01:14:48 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'auth':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/auth.controller');
|
2023-07-13 01:14:48 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2024-08-01 02:02:36 -07:00
|
|
|
case 'oauth2':
|
2024-08-27 07:44:32 -07:00
|
|
|
await import('@/controllers/oauth/oauth2-credential.controller');
|
2024-08-01 02:02:36 -07:00
|
|
|
break;
|
|
|
|
|
2023-08-23 19:59:16 -07:00
|
|
|
case 'mfa':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/mfa.controller');
|
2023-11-08 07:29:39 -08:00
|
|
|
break;
|
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'ldap':
|
2024-08-22 02:10:37 -07:00
|
|
|
const { LdapService } = await import('@/ldap/ldap.service.ee');
|
|
|
|
await import('@/ldap/ldap.controller.ee');
|
2023-11-30 00:23:09 -08:00
|
|
|
testServer.license.enable('feat:ldap');
|
2024-01-15 06:01:48 -08:00
|
|
|
await Container.get(LdapService).init();
|
2023-07-13 01:14:48 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'saml':
|
2024-08-22 02:10:37 -07:00
|
|
|
const { setSamlLoginEnabled } = await import('@/sso/saml/saml-helpers');
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/sso/saml/routes/saml.controller.ee');
|
2023-07-13 01:14:48 -07:00
|
|
|
await setSamlLoginEnabled(true);
|
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'sourceControl':
|
2024-08-26 02:10:06 -07:00
|
|
|
await import('@/environments/source-control/source-control.controller.ee');
|
2023-07-13 01:14:48 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-10-09 07:09:23 -07:00
|
|
|
case 'community-packages':
|
2024-08-27 07:44:32 -07:00
|
|
|
await import('@/controllers/community-packages.controller');
|
2023-11-08 07:29:39 -08:00
|
|
|
break;
|
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'me':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/me.controller');
|
2023-07-13 01:14:48 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'passwordReset':
|
2024-08-27 07:44:32 -07:00
|
|
|
await import('@/controllers/password-reset.controller');
|
2023-07-13 01:14:48 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'owner':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/owner.controller');
|
2023-07-13 01:14:48 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
case 'users':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/users.controller');
|
2023-07-20 02:34:45 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-11-16 09:39:43 -08:00
|
|
|
case 'invitations':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/invitation.controller');
|
2023-11-16 09:39:43 -08:00
|
|
|
break;
|
|
|
|
|
2023-07-20 02:34:45 -07:00
|
|
|
case 'tags':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/tags.controller');
|
2023-07-20 02:34:45 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-08-25 01:33:46 -07:00
|
|
|
case 'externalSecrets':
|
2024-08-22 02:10:37 -07:00
|
|
|
await import('@/external-secrets/external-secrets.controller.ee');
|
2023-08-25 01:33:46 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-09-27 07:22:39 -07:00
|
|
|
case 'workflowHistory':
|
2024-08-22 02:10:37 -07:00
|
|
|
await import('@/workflows/workflow-history/workflow-history.controller.ee');
|
2023-09-27 07:22:39 -07:00
|
|
|
break;
|
2023-11-08 07:29:39 -08:00
|
|
|
|
2023-10-06 07:21:13 -07:00
|
|
|
case 'binaryData':
|
2024-08-27 07:44:32 -07:00
|
|
|
await import('@/controllers/binary-data.controller');
|
2023-10-06 07:21:13 -07:00
|
|
|
break;
|
2023-11-28 05:16:47 -08:00
|
|
|
|
2023-12-12 06:18:32 -08:00
|
|
|
case 'debug':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/debug.controller');
|
2023-12-12 06:18:32 -08:00
|
|
|
break;
|
2024-05-17 01:53:15 -07:00
|
|
|
|
|
|
|
case 'project':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/project.controller');
|
2024-05-17 01:53:15 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'role':
|
2024-06-19 00:57:40 -07:00
|
|
|
await import('@/controllers/role.controller');
|
2024-05-17 01:53:15 -07:00
|
|
|
break;
|
2024-05-21 10:11:02 -07:00
|
|
|
|
|
|
|
case 'dynamic-node-parameters':
|
2024-08-27 07:44:32 -07:00
|
|
|
await import('@/controllers/dynamic-node-parameters.controller');
|
2024-05-21 10:11:02 -07:00
|
|
|
break;
|
2023-07-13 01:14:48 -07:00
|
|
|
}
|
|
|
|
}
|
2024-06-19 00:57:40 -07:00
|
|
|
|
|
|
|
Container.get(ControllerRegistry).activate(app);
|
2023-07-13 01:14:48 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
afterAll(async () => {
|
|
|
|
await testDb.terminate();
|
|
|
|
testServer.httpServer.close();
|
|
|
|
});
|
|
|
|
|
2023-11-30 00:23:09 -08:00
|
|
|
beforeEach(() => {
|
|
|
|
testServer.license.reset();
|
|
|
|
});
|
|
|
|
|
2023-07-13 01:14:48 -07:00
|
|
|
return testServer;
|
|
|
|
};
|