@camunda/e2e-test-suite 0.0.755 → 0.0.756
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fixtures/8.10.d.ts +2 -0
- package/dist/fixtures/8.10.js +4 -0
- package/dist/pages/8.10/AuditLogPage.d.ts +14 -0
- package/dist/pages/8.10/AuditLogPage.js +53 -0
- package/dist/tests/8.10/orchestration-cluster-mcp-server/api/mcp-processes-audit-log.spec.d.ts +1 -0
- package/dist/tests/8.10/orchestration-cluster-mcp-server/api/mcp-processes-audit-log.spec.js +146 -0
- package/dist/tests/8.10/test-setup.spec.js +3 -1
- package/dist/utils/constants.d.ts +4 -0
- package/dist/utils/constants.js +7 -1
- package/package.json +1 -1
package/dist/fixtures/8.10.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { OCIdentityRolesPage } from '../pages/8.10/OCIdentityRolesPage';
|
|
|
34
34
|
import { OCIdentityAuthorizationsPage } from '../pages/8.10/OCIdentityAuthorizationsPage';
|
|
35
35
|
import { OCIdentityClusterVariablesPage } from '../pages/8.10/OCIdentityClusterVariablesPage';
|
|
36
36
|
import { ClientCredentialsDetailsPage } from '../pages/8.10/ClientCredentialsDetailsPage';
|
|
37
|
+
import { AuditLogPage } from '../pages/8.10/AuditLogPage';
|
|
37
38
|
type PlaywrightFixtures = {
|
|
38
39
|
makeAxeBuilder: () => AxeBuilder;
|
|
39
40
|
loginPage: LoginPage;
|
|
@@ -71,6 +72,7 @@ type PlaywrightFixtures = {
|
|
|
71
72
|
ocIdentityAuthorizationsPage: OCIdentityAuthorizationsPage;
|
|
72
73
|
ocIdentityClusterVariablesPage: OCIdentityClusterVariablesPage;
|
|
73
74
|
clientCredentialsDetailsPage: ClientCredentialsDetailsPage;
|
|
75
|
+
auditLogPage: AuditLogPage;
|
|
74
76
|
overrideTrackingScripts: void;
|
|
75
77
|
};
|
|
76
78
|
declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & PlaywrightFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
package/dist/fixtures/8.10.js
CHANGED
|
@@ -41,6 +41,7 @@ const OCIdentityRolesPage_1 = require("../pages/8.10/OCIdentityRolesPage");
|
|
|
41
41
|
const OCIdentityAuthorizationsPage_1 = require("../pages/8.10/OCIdentityAuthorizationsPage");
|
|
42
42
|
const OCIdentityClusterVariablesPage_1 = require("../pages/8.10/OCIdentityClusterVariablesPage");
|
|
43
43
|
const ClientCredentialsDetailsPage_1 = require("../pages/8.10/ClientCredentialsDetailsPage");
|
|
44
|
+
const AuditLogPage_1 = require("../pages/8.10/AuditLogPage");
|
|
44
45
|
const test = test_1.test.extend({
|
|
45
46
|
makeAxeBuilder: async ({ page }, use) => {
|
|
46
47
|
const makeAxeBuilder = () => new playwright_1.default({ page }).withTags([
|
|
@@ -158,6 +159,9 @@ const test = test_1.test.extend({
|
|
|
158
159
|
clientCredentialsDetailsPage: async ({ page }, use) => {
|
|
159
160
|
await use(new ClientCredentialsDetailsPage_1.ClientCredentialsDetailsPage(page));
|
|
160
161
|
},
|
|
162
|
+
auditLogPage: async ({ page }, use) => {
|
|
163
|
+
await use(new AuditLogPage_1.AuditLogPage(page));
|
|
164
|
+
},
|
|
161
165
|
overrideTrackingScripts: [
|
|
162
166
|
async ({ context }, use) => {
|
|
163
167
|
await context.route('https://cmp.osano.com/16CVvwSNKHi9t1grQ/9403708a-488b-4f3b-aea6-613825dec79f/osano.js', (route) => route.fulfill({
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
type AuditLogCategory = 'Administration' | 'Deployed resources' | 'User tasks';
|
|
3
|
+
declare class AuditLogPage {
|
|
4
|
+
readonly auditLogTab: Locator;
|
|
5
|
+
readonly updateButton: Locator;
|
|
6
|
+
readonly clientOperationsSection: Locator;
|
|
7
|
+
readonly clientCategoryCheckbox: (category: AuditLogCategory) => Locator;
|
|
8
|
+
readonly clientCategoryLabel: (category: AuditLogCategory) => Locator;
|
|
9
|
+
constructor(page: Page);
|
|
10
|
+
clickAuditLogTab(): Promise<void>;
|
|
11
|
+
private ensureChecked;
|
|
12
|
+
enableClientOperationLogging(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export { AuditLogPage };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuditLogPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class AuditLogPage {
|
|
6
|
+
auditLogTab;
|
|
7
|
+
updateButton;
|
|
8
|
+
clientOperationsSection;
|
|
9
|
+
clientCategoryCheckbox;
|
|
10
|
+
clientCategoryLabel;
|
|
11
|
+
constructor(page) {
|
|
12
|
+
this.auditLogTab = page.getByRole('tab', { name: 'Audit log' });
|
|
13
|
+
const auditLogPanel = page.getByRole('tabpanel', { name: 'Audit log' });
|
|
14
|
+
this.updateButton = auditLogPanel.getByRole('button', {
|
|
15
|
+
name: 'Update',
|
|
16
|
+
exact: true,
|
|
17
|
+
});
|
|
18
|
+
// Scope to the Client block: the category labels also exist under the User section.
|
|
19
|
+
this.clientOperationsSection = auditLogPanel
|
|
20
|
+
.getByText('Client operations to log', { exact: true })
|
|
21
|
+
.locator('..');
|
|
22
|
+
this.clientCategoryCheckbox = (category) => this.clientOperationsSection.getByRole('checkbox', { name: category });
|
|
23
|
+
// Carbon hides the real <input>; toggle via the visible label.
|
|
24
|
+
this.clientCategoryLabel = (category) => this.clientOperationsSection.locator('label').filter({ hasText: category });
|
|
25
|
+
}
|
|
26
|
+
async clickAuditLogTab() {
|
|
27
|
+
await (0, test_1.expect)(this.auditLogTab).toBeVisible({ timeout: 60000 });
|
|
28
|
+
await this.auditLogTab.click({ timeout: 60000 });
|
|
29
|
+
}
|
|
30
|
+
async ensureChecked(category) {
|
|
31
|
+
const checkbox = this.clientCategoryCheckbox(category);
|
|
32
|
+
await (0, test_1.expect)(checkbox).toBeAttached({ timeout: 30000 });
|
|
33
|
+
if (!(await checkbox.isChecked())) {
|
|
34
|
+
await this.clientCategoryLabel(category).click({ timeout: 30000 });
|
|
35
|
+
await (0, test_1.expect)(checkbox).toBeChecked();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async enableClientOperationLogging() {
|
|
39
|
+
const categories = [
|
|
40
|
+
'Administration',
|
|
41
|
+
'Deployed resources',
|
|
42
|
+
'User tasks',
|
|
43
|
+
];
|
|
44
|
+
for (const category of categories) {
|
|
45
|
+
await this.ensureChecked(category);
|
|
46
|
+
}
|
|
47
|
+
if (await this.updateButton.isEnabled()) {
|
|
48
|
+
await this.updateButton.click({ timeout: 60000 });
|
|
49
|
+
await (0, test_1.expect)(this.updateButton).toBeDisabled({ timeout: 60000 });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.AuditLogPage = AuditLogPage;
|
package/dist/tests/8.10/orchestration-cluster-mcp-server/api/mcp-processes-audit-log.spec.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
|
|
4
|
+
* one or more contributor license agreements. See the NOTICE file distributed
|
|
5
|
+
* with this work for additional information regarding copyright ownership.
|
|
6
|
+
* Licensed under the Camunda License 1.0. You may not use this file
|
|
7
|
+
* except in compliance with the Camunda License 1.0.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
/*
|
|
11
|
+
* MCP audit-log inbound channel provenance — camunda/camunda#56234
|
|
12
|
+
* (SC-AUD-01/02/03).
|
|
13
|
+
*/
|
|
14
|
+
const _8_10_1 = require("../../../../fixtures/8.10");
|
|
15
|
+
const test_1 = require("@playwright/test");
|
|
16
|
+
const apiHelpers_1 = require("../../../../utils/apiHelpers");
|
|
17
|
+
const mcpHelpers_1 = require("../../../../utils/mcpHelpers");
|
|
18
|
+
const constants_1 = require("../../../../utils/constants");
|
|
19
|
+
// Raw BPMN `io.camunda.tool:name` annotation — what the audit log stores in
|
|
20
|
+
// inboundChannelToolName (not the composite tools/list name).
|
|
21
|
+
const TOOL_NAME_RAW = 'mcp-as-tool-process';
|
|
22
|
+
const MCP_PROCESS_DEFINITION_ID = 'mcpAsToolProcess';
|
|
23
|
+
let authToken;
|
|
24
|
+
let mcpProcessesUrl;
|
|
25
|
+
let messageSubscriptionsSearchUrl;
|
|
26
|
+
let auditLogsSearchUrl;
|
|
27
|
+
let mcpSessionId = null;
|
|
28
|
+
let restProcessDefinitionKey;
|
|
29
|
+
let compositeToolName;
|
|
30
|
+
_8_10_1.test.describe.configure({ mode: 'parallel' });
|
|
31
|
+
_8_10_1.test.describe('Orchestration Cluster MCP Server — audit log inbound channel', () => {
|
|
32
|
+
_8_10_1.test.beforeAll(async () => {
|
|
33
|
+
authToken = await (0, apiHelpers_1.authSaasAPI)(undefined, 'agentic_cluster');
|
|
34
|
+
mcpProcessesUrl = (0, apiHelpers_1.buildZeebeApiUrl)('/mcp/processes', 'saas', 'agentic_cluster');
|
|
35
|
+
messageSubscriptionsSearchUrl = (0, apiHelpers_1.buildZeebeApiUrl)('/v2/message-subscriptions/search', 'saas', 'agentic_cluster');
|
|
36
|
+
auditLogsSearchUrl = (0, apiHelpers_1.buildZeebeApiUrl)('/v2/audit-logs/search', 'saas', 'agentic_cluster');
|
|
37
|
+
const apiContext = await (0, apiHelpers_1.getApiRequestContext)();
|
|
38
|
+
mcpSessionId = await (0, mcpHelpers_1.initializeMcpSession)(apiContext, mcpProcessesUrl, authToken, mcpSessionId);
|
|
39
|
+
const [mcpKey, restKey] = await Promise.all([
|
|
40
|
+
(0, apiHelpers_1.deployProcess)('./resources/mcp_server_saas/mcp_as_tool_process.bpmn', authToken, 'saas', 'agentic_cluster'),
|
|
41
|
+
(0, apiHelpers_1.deployProcess)('./resources/mcp_server_saas/mcp_process_instance_test_file.bpmn', authToken, 'saas', 'agentic_cluster'),
|
|
42
|
+
]);
|
|
43
|
+
if (mcpKey == null) {
|
|
44
|
+
throw new Error('Failed to deploy mcp_as_tool_process.bpmn');
|
|
45
|
+
}
|
|
46
|
+
if (restKey == null) {
|
|
47
|
+
throw new Error('Failed to deploy mcp_process_instance_test_file.bpmn');
|
|
48
|
+
}
|
|
49
|
+
restProcessDefinitionKey = String(restKey);
|
|
50
|
+
// Composite tool name is `<toolName>_<messageSubscriptionKey>`.
|
|
51
|
+
await (0, test_1.expect)(async () => {
|
|
52
|
+
const res = await apiContext.post(messageSubscriptionsSearchUrl, {
|
|
53
|
+
headers: (0, mcpHelpers_1.buildMcpHeaders)(authToken, mcpSessionId),
|
|
54
|
+
data: {
|
|
55
|
+
filter: {
|
|
56
|
+
processDefinitionId: MCP_PROCESS_DEFINITION_ID,
|
|
57
|
+
messageSubscriptionType: 'START_EVENT',
|
|
58
|
+
},
|
|
59
|
+
sort: [{ field: 'processDefinitionVersion', order: 'desc' }],
|
|
60
|
+
page: { limit: 1 },
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
await (0, mcpHelpers_1.assertResponseStatus)(res, 200);
|
|
64
|
+
const json = (await res.json());
|
|
65
|
+
(0, test_1.expect)(json.items.length).toBeGreaterThanOrEqual(1);
|
|
66
|
+
compositeToolName = `${TOOL_NAME_RAW}_${json.items[0].messageSubscriptionKey}`;
|
|
67
|
+
}).toPass(constants_1.deletionAssertionOptions);
|
|
68
|
+
});
|
|
69
|
+
(0, _8_10_1.test)('SC-AUD-01 — MCP tools/call produces audit entry with inboundChannelType=MCP', async () => {
|
|
70
|
+
const apiContext = await (0, apiHelpers_1.getApiRequestContext)();
|
|
71
|
+
const callBody = await (0, mcpHelpers_1.callTool)(apiContext, mcpProcessesUrl, authToken, mcpSessionId, compositeToolName, {});
|
|
72
|
+
(0, test_1.expect)(callBody.result).toBeDefined();
|
|
73
|
+
(0, test_1.expect)(callBody.result.isError).not.toBe(true);
|
|
74
|
+
const pik = (0, mcpHelpers_1.extractProcessInstanceKey)(callBody.result.content[0]?.text ?? '');
|
|
75
|
+
(0, test_1.expect)(pik, 'Expected processInstanceKey from MCP tools/call').not.toBeNull();
|
|
76
|
+
await (0, test_1.expect)(async () => {
|
|
77
|
+
const res = await apiContext.post(auditLogsSearchUrl, {
|
|
78
|
+
headers: (0, mcpHelpers_1.buildMcpHeaders)(authToken, mcpSessionId),
|
|
79
|
+
data: {
|
|
80
|
+
filter: {
|
|
81
|
+
processInstanceKey: pik,
|
|
82
|
+
operationType: 'CREATE',
|
|
83
|
+
},
|
|
84
|
+
page: { limit: 10 },
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
await (0, mcpHelpers_1.assertResponseStatus)(res, 200);
|
|
88
|
+
const json = (await res.json());
|
|
89
|
+
const entry = json.items.find((i) => i.entityType === 'PROCESS_INSTANCE');
|
|
90
|
+
(0, test_1.expect)(entry, `Expected a PROCESS_INSTANCE CREATE audit entry for pik ${pik}`).toBeDefined();
|
|
91
|
+
(0, test_1.expect)(entry.inboundChannelType).toBe('MCP');
|
|
92
|
+
}).toPass(constants_1.auditLogAssertionOptions);
|
|
93
|
+
});
|
|
94
|
+
(0, _8_10_1.test)('SC-AUD-02 — MCP audit entry captures the raw tool name in inboundChannelToolName', async () => {
|
|
95
|
+
const apiContext = await (0, apiHelpers_1.getApiRequestContext)();
|
|
96
|
+
const callBody = await (0, mcpHelpers_1.callTool)(apiContext, mcpProcessesUrl, authToken, mcpSessionId, compositeToolName, {});
|
|
97
|
+
(0, test_1.expect)(callBody.result).toBeDefined();
|
|
98
|
+
(0, test_1.expect)(callBody.result.isError).not.toBe(true);
|
|
99
|
+
const pik = (0, mcpHelpers_1.extractProcessInstanceKey)(callBody.result.content[0]?.text ?? '');
|
|
100
|
+
(0, test_1.expect)(pik).not.toBeNull();
|
|
101
|
+
await (0, test_1.expect)(async () => {
|
|
102
|
+
const res = await apiContext.post(auditLogsSearchUrl, {
|
|
103
|
+
headers: (0, mcpHelpers_1.buildMcpHeaders)(authToken, mcpSessionId),
|
|
104
|
+
data: {
|
|
105
|
+
filter: {
|
|
106
|
+
processInstanceKey: pik,
|
|
107
|
+
operationType: 'CREATE',
|
|
108
|
+
},
|
|
109
|
+
page: { limit: 10 },
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
await (0, mcpHelpers_1.assertResponseStatus)(res, 200);
|
|
113
|
+
const json = (await res.json());
|
|
114
|
+
const entry = json.items.find((i) => i.entityType === 'PROCESS_INSTANCE');
|
|
115
|
+
(0, test_1.expect)(entry, `Expected a PROCESS_INSTANCE CREATE audit entry for pik ${pik}`).toBeDefined();
|
|
116
|
+
// Raw annotation value, not the composite tools/list name.
|
|
117
|
+
(0, test_1.expect)(entry.inboundChannelToolName).toBe(TOOL_NAME_RAW);
|
|
118
|
+
(0, test_1.expect)(entry.inboundChannelType).toBe('MCP');
|
|
119
|
+
}).toPass(constants_1.auditLogAssertionOptions);
|
|
120
|
+
});
|
|
121
|
+
(0, _8_10_1.test)('SC-AUD-03 — REST-triggered start is distinguishable from MCP (inboundChannelType and tool name both null)', async () => {
|
|
122
|
+
const apiContext = await (0, apiHelpers_1.getApiRequestContext)();
|
|
123
|
+
// NONE-start BPMN started via REST — bypasses MCP entirely.
|
|
124
|
+
const restPik = await (0, apiHelpers_1.createProcessInstance)(restProcessDefinitionKey, authToken, 'saas', 'agentic_cluster');
|
|
125
|
+
(0, test_1.expect)(restPik).toBeTruthy();
|
|
126
|
+
await (0, test_1.expect)(async () => {
|
|
127
|
+
const res = await apiContext.post(auditLogsSearchUrl, {
|
|
128
|
+
headers: (0, mcpHelpers_1.buildMcpHeaders)(authToken, mcpSessionId),
|
|
129
|
+
data: {
|
|
130
|
+
filter: {
|
|
131
|
+
processInstanceKey: restPik,
|
|
132
|
+
operationType: 'CREATE',
|
|
133
|
+
},
|
|
134
|
+
page: { limit: 10 },
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
await (0, mcpHelpers_1.assertResponseStatus)(res, 200);
|
|
138
|
+
const json = (await res.json());
|
|
139
|
+
const entry = json.items.find((i) => i.entityType === 'PROCESS_INSTANCE');
|
|
140
|
+
(0, test_1.expect)(entry, `Expected a PROCESS_INSTANCE CREATE audit entry for REST pik ${restPik}`).toBeDefined();
|
|
141
|
+
// REST-triggered entries have both fields null (verified on 8.10-SNAPSHOT).
|
|
142
|
+
(0, test_1.expect)(entry.inboundChannelType).toBeNull();
|
|
143
|
+
(0, test_1.expect)(entry.inboundChannelToolName).toBeNull();
|
|
144
|
+
}).toPass(constants_1.auditLogAssertionOptions);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -47,7 +47,7 @@ _8_10_1.test.describe('Cluster Setup Tests', () => {
|
|
|
47
47
|
await clusterSecretsPage.createSetOfSecrets(clusterName, connectorSecrets_1.bedrockCodeInterpreterSecretsValues);
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
|
-
(0, _8_10_1.test)('Create Agentic Orchestration Cluster', async ({ page, loginPage, homePage, clusterPage, clusterDetailsPage, clusterSecretsPage, }, testInfo) => {
|
|
50
|
+
(0, _8_10_1.test)('Create Agentic Orchestration Cluster', async ({ page, loginPage, homePage, clusterPage, clusterDetailsPage, clusterSecretsPage, auditLogPage, }, testInfo) => {
|
|
51
51
|
_8_10_1.test.skip(process.env.IS_PROD === 'true', 'Skipping test because not required on PROD test org');
|
|
52
52
|
_8_10_1.test.skip(process.env.IS_AG === 'true', 'Skipping test because not required when IS_AG is true');
|
|
53
53
|
_8_10_1.test.slow();
|
|
@@ -60,6 +60,8 @@ _8_10_1.test.describe('Cluster Setup Tests', () => {
|
|
|
60
60
|
await clusterPage.assertClusterHealthyStatusWithRetry(clusterName, 300000, 1500000);
|
|
61
61
|
await clusterPage.clickClusterLink(clusterName);
|
|
62
62
|
await clusterDetailsPage.assertComponentsHealth();
|
|
63
|
+
await auditLogPage.clickAuditLogTab();
|
|
64
|
+
await auditLogPage.enableClientOperationLogging();
|
|
63
65
|
await clusterDetailsPage.clickSettingsTab();
|
|
64
66
|
await clusterDetailsPage.checkMcpSupportToggle();
|
|
65
67
|
await homePage.clickClusters();
|
|
@@ -10,6 +10,10 @@ export declare const deletionAssertionOptions: {
|
|
|
10
10
|
intervals: number[];
|
|
11
11
|
timeout: number;
|
|
12
12
|
};
|
|
13
|
+
export declare const auditLogAssertionOptions: {
|
|
14
|
+
intervals: number[];
|
|
15
|
+
timeout: number;
|
|
16
|
+
};
|
|
13
17
|
export declare const TIMEOUT: {
|
|
14
18
|
short: number;
|
|
15
19
|
medium: number;
|
package/dist/utils/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TIMEOUT = exports.deletionAssertionOptions = exports.defaultAssertionOptions = exports._3_MINUTES_IN_MS = exports._1_MINUTE_IN_MS = exports._1_SECOND_IN_MS = exports._100_MS = void 0;
|
|
3
|
+
exports.TIMEOUT = exports.auditLogAssertionOptions = exports.deletionAssertionOptions = exports.defaultAssertionOptions = exports._3_MINUTES_IN_MS = exports._1_MINUTE_IN_MS = exports._1_SECOND_IN_MS = exports._100_MS = void 0;
|
|
4
4
|
exports._100_MS = 100;
|
|
5
5
|
exports._1_SECOND_IN_MS = 1000;
|
|
6
6
|
exports._1_MINUTE_IN_MS = 60 * 1000;
|
|
@@ -14,6 +14,12 @@ exports.deletionAssertionOptions = {
|
|
|
14
14
|
intervals: [5000, 10000, 15000, 30000],
|
|
15
15
|
timeout: 60000,
|
|
16
16
|
};
|
|
17
|
+
// Longest options for audit-log assertions: entries land in secondary storage
|
|
18
|
+
// only after the audit config propagates to the exporters.
|
|
19
|
+
exports.auditLogAssertionOptions = {
|
|
20
|
+
intervals: [5000, 10000, 15000, 15000],
|
|
21
|
+
timeout: 90000,
|
|
22
|
+
};
|
|
17
23
|
exports.TIMEOUT = {
|
|
18
24
|
short: 15000,
|
|
19
25
|
medium: 30000,
|